Unit3-窝窝社交圈
全文共4909字,推荐阅读时间15~20分钟。
文章共分五个部分:
JML总结
定义
JML是一种对Java程序进行规格化设计的表示语言JML是一种行为接口规格语言(Behavior Interface Specification Language,BISL)
注释结构
JML和Javadoc的注释方式相同- 每行都以@起头
- 行注释:
//@ annotation - 块注释:
/*@ annotation @*/
//@ public model non_null int[] elements
/*@ public normal_behavior
@ ensures \result == elements.length;
@*/
model:表示elements数组是规格层次的描述,不是JML语句所在类的组成部分,也不要求该类声明这一变量。non_null:elemants数组对象的引用不能是null- 作用域:和Java相同,
JML也有自己的作用域,此处是public.
- 规格中每个子句需要以分号结尾
- 在接口中声明规格变量时,要求明确变量的类别。
- 静态
- 实例
// inside an interface
//@ public static model non_null int[] elements
//@ public instance model non_null int[] elements
表达式
JML断言中,不可以使用带有赋值语义的操作符。例如,++,--,+=等。
原子表达式
\result:表示一个非void的方法的返回值\old(expr):表达式expr在执行相应方法前的取值- 当
expr是引用时,表示的是指向的对象(地址)有没有发生变化,而不是对象本身有没有发生变化。因此,当对象发生变化时,expr只要依然指向它,就没有发生变化。 map.size() = \old(map).size() != \old(map.size())
- 当
\not_assigned(x,y,...):括号中变量如果在执行过程中被赋值则返回true,否则为false.\not_modified(x,y,...):类似not_assigned(),追踪变量值在执行过程中是否发生变化。相比
\not_assigned(x,y,...)使用较多,通常用在前置条件和后置条件中,断言变量在进入函数前后是否相等。\nonnullelements(container):断言container对象中存储的对象没有nullcontainer != null && (\forall int i; 0<=i && i < container.length; container[i]!=null)
\type(type):返回基本类型type对应的引用类型,例如type(boolean)的值为Boolean.TYPE.TYPE等价于java.lang.Class\typeof(expr):返回表达式expr对应的准确类型,例如\typeof(false)的值为Boolean.TYPE.
量化表达式
\forall:全称量词修饰的逻辑表达式,表示给定范围内的元素如果都满足相应需求则为真。(\forall int i,j; 0<=i && i < j && j < 10; a[i] < a[j]),当a是严格单增的数组时该表达式为真。
\exists:存在量词修饰的逻辑表达式,表示如果存在给定范围内的元素满足相应需求则为真。(\exists int i; 0 <= i && i < 10; a[i] < 0),当数组中有小于0的元素时表达式为真。
\sum:返回给定范围内指定的求和表达式的和(\sum int i; 0 <= i && i < 5; i)即\(0+1+2+3+4\)(\sum int i; 0 <= i && i < 5; i * i)即\(0^2+1^2+2^2+3^2+4^2\)
\product:返回给定范围内指定的求积表达式的积\max:返回给定范围内指定表达式的最大值(\max int i; 0 <= i && i < 5; i)
\min:返回给定范围内指定表达式的最小值\num_of:返回给定范围内满足相应条件的表达式个数(\num_of int x; 0 < x && x <=20; x % 2 ==0)
集合表达式
在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。
new ST {T x | R(x) && P(x)},其中R(x)对应集合中x的范围,P(x)对应x取值的约束。new JMLObjectSet {Integer i | s.contains(i) && 0 < i.intValue() }
s指java程序中构造的容器,如ArrayList.
操作符
\(JML操作符=Java操作符+子类型关系+等价关系+推理+变量引用\)
子类型关系操作符:
E1<:E2,当E1是E2的子类型时,表达式结果为真。(包括两者是相同类型)等价关系操作符:
b_expr1<==>b_expr2和b_expr1<=!=>b_expr2,其中表达式均为布尔表达式。<==>和<=!=>的优先级低于==和!=推理操作符:
b_expr1==>b_expr2和b_expr1<==b_expr2,和离散中的谓词表达式的真值判断相同。变量引用操作符:
JML规格语句可以直接引用Java代码或者JML规格中定义的变量,使用\nothing,\everything来概括作用域,常出现在assignable句子中。assignable \nothing表示当前作用域下每个变量都不可以在方法执行过程中被赋值。
模型域(model)
理解为规格中的成员变量
//@ public model instance JMLObject elemQueue;
创建了一个公开的、类型为
JMLObject的、名叫elemQueue的模型实例
instance:虽然这个模型是在接口中被定义出来的,但是每个实现接口的类都有各自的模型域。- 声明的模型域和Java代码无关,不可以在Java代码中被引用。
model为Java代码提供了一个建议,如果Java代码中实现了相应的变量,那么就可以使用JML进行检查,但是如果没有实现,则只是提供一个建议。
模型域的取值(represents)
用于沟通JML和Java
//@ private represents jmlHeap <- javaHeap;
实现了jmlHeap和javaHeap的同步,每次永不需要手动操作。
方法规格
基本概念
- 前置条件:对方法输入参数的限制
- 后置条件:对方法执行结果的限制
- 副作用:方法在执行过程中对输入对象或者
this对象进行了修改(赋值等操作)
前置条件(pre-condition)
- 通过
requires子句表示requires P;,其中P是逻辑表达式(可以包含逻辑连接词如&&,||,!)
后置条件(post-condition)
- 通过
ensures子句表示ensures P;,其中P是逻辑表达式
副作用(side-effects)
- 约束副作用范围:
assignable,modifiable(没有\)assignable elements,max,min,多个变量时使用,隔开。
JML不允许在约束语句中指定规格中声明的变量数据- 二者可以替换,但是通常使用
assignable.
纯粹访问:
purepublic /*@ pure @*/ int getCredits();
方法规格描述可以省略
\assignable
类型规格
对Java程序中定义的数据类型设计的限制规则,通常是对类、接口设计的约束规则。
不变式限制(invariant)
不变式是要求在所有可见状态下都要满足的特性。
invariant P:invariant是关键词,P是谓词。可见状态(visible state):凡是会修改成员变量(静态+实例)的方法的执行期间,对象都处于不可见状态。
这些方法在执行时,对象状态不稳定,因此不可见。
public class Path {
private /*@ spec_public @*/ ArrayList<Integer> seq_nodes;
private /*@ spec_public @*/ Integer start_node;
private /*@ spec_public @*/ Integer end_node;
/*@ invariant seq_nodes !=null &&
@ seq_nodes[0] == start_node &&
@ seq_nodes[seq_nodes.length - 1] == end_node &&
@ seq_nodes.length >= 2;
@*/
}
spec_public表示private属性的成员变量在规格中对调用者可见,在Java程序中的可见性不受影响。invariant语句最后以;结尾
不变式中可以直接引用
pure形态的方法静态不变式(static invariant):只约束静态成员变量
实例不变式(instance invariant):约束静态+非静态成员变量
实例
前置条件调用
pure方法/*@ requires c >= 0;
@ ensures getCredits() == \old(getCredits()) + c;
@*/
public void addCredits(int c);
使用量化表达式进行限制
/*@ ensures !contains(elem);
@ ensures (\forall int e; e != elem; contains(e) <==> \old(contains(e)));
@ ensures \old(contains(elem)) ==> size == \old(size) - 1;
@ ensures !\old(contains(elem)) ==> size == \old(size);
@*/
public void remove(int elem);
signals子句&signals_only子句
public abstract class Student {
// public model non_null int[] credits;
/*@ public normal_behavior
@ requires z >= 0 && z <= 100;
@ assignable \nothing;
@ ensures \result == credits.length; @ also
@ public exceptional_behavior
@ requires z < 0;
@ assignable \nothing;
@ signals_only IllegalArgumentException; @ also
@ public exceptional_behavior
@ requires z > 100;
@ assignable \nothing;
@ signals (OverFlowException e) true;
@*/
public abstract int recordCredit(int z) throws IllegalArgumentException,OverFlowException;
}
normal_behavior/exceptional_behavior:后面不用带;also使用情景:- 子类覆写父类方法后,进行规格补充时。
- 分隔正常功能和异常功能
also各个块的前置条件不能有重合,因为各部分不是串行的。signals (Exception e) b_expr:当设置b_expr为true时,方法需要抛出相应的异常。在异常功能中,
ensures语句一样可以使用。signals_only Exception:不关心什么条件下会抛出,只要方法抛出即可。
状态变化约束(constraint)
状态变化约束是在限制状态变化的方式,在前序可见状态和当前可见状态中生效。
public class ServiceCounter {
private /*@spec_public@*/ long counter;
//@ invariant counter >= 0;
//@ constraint counter == \old(counter) + 1;
}
constraint限制可以在每个改变counter的方法的后置条件中实现,但是相比直接在变量本身添加限制会更繁琐。
- 静态状态约束(static constraint)
- 实例状态约束(instance constraint)
方法规格&类型规格
- 当一个类是不变类时,构造方法的后置条件和成员变量的不定式选一个实现即可。
JML FAQ
JML只对纯方法支持断言确认当方法修改引用的具体域时,需要指明是哪个域。
/*@ assignable obj.x
@*/
public void foo(Bar obj){
// TODO
}
JML中==和equals的区别两者的区别和Java中的区别相同
在
exceptional_behavior情况下,可以定义方法的前置条件、副作用和异常抛出,但不能定义方法的后置条件。使用
signals时,可以不需要定义exceptional_behavior的前置条件。JML中是否可以引用Java中的变量JML无论何时都可以调用Java中的pure方法,但是并不是随时都能够引用变量。- 如果Java中的变量在
JML对应方法的参数列表、方法体中出现,那么就可以引用。 /*@ spec_public @*/的作用就是让不满足第二条的变量能够被调用者的JML引用。- 如果第三条也不满足,即
JML对应的方法以及其调用的方法中都没有出现需要的变量,则不能够引用相应的变量。
引用变量则可以使用其方法
JML工具链
SMT solver
Z3 Theorem Prover is a cross-platform satisfiability modulo theories (SMT) solver by Microsoft.
--Wikipedia
因为openjml开发较早,所以导致很多我们现在使用的JML语法无法被识别,因此实现了基本的四则运算进行验证。
方法如下:
public class Test {
// @ensures \result == a + b;
public static int add(int a, int b) {
return a + b;
}
// @ensures \result == a - b;
public static int sub(int a, int b) {
return a - b;
}
// @ensures \result == a * b;
public static int mult(int a, int b) {
return a * b;
}
// @ensures \result == a / b;
public static int div(int a, int b) {
return a / b;
}
}
SMT solver反馈结果如下:
java -jar .\openjml.jar -esc -prover .\z3-4.7.1.exe -exec .\z3-4.7.1.exe .\Test.java
错误: An error while executing a proof script for add: (error "Error writing to solver: (set-logic ALL) java.io.IOException: 管道正在被关闭。")
错误: An error while executing a proof script for div: (error "Error writing to solver: (set-option :AUTO_CONFIG false) java.io.IOException: 管道正在被关闭。")
错误: An error while executing a proof script for mult: (error "Error writing to solver: (set-option :smt.MBQI false) java.io.IOException: 管道正在被关闭。")
错误: An error while executing a proof script for sub: (error "Error writing to solver: (declare-sort REF 0) java.io.IOException: 管道正在被关闭。")
4 个错误
JMLUnitNG部署与测试
由于JMLUnitNG支持的JML语法较少,与现在我们使用的大部分语法并不兼容,因此将Group中的部分方法及其规格进行简化后进行了自动生成数据并测试的试验。
依次键入以下命令:
java -jar jmlunitng-1_4.jar Group.java
javac -cp jmlunitng-1_4.jar *.java
openjml -rac Group.java
java -cp jmlunitng-1_4.jar Group_JML_Test
对add方法得到如下反馈结果
[TestNG] Running:
Command line suite
Passed: racEnabled()
Passed: constructor Group()
Failed: static add(-2147483648, -2147483648)
Passed: static add(0, -2147483648)
Passed: static add(2147483647, -2147483648)
Passed: static add(-2147483648, 0)
Passed: static add(0, 0)
Passed: static add(2147483647, 0)
Passed: static add(-2147483648, 2147483647)
Passed: static add(0, 2147483647)
Failed: static add(2147483647, 2147483647)
===============================================
Command line suite
Total tests run: 11, Failures: 2, Skips: 0
===============================================
可以看到自动生成的数据均为边界数据,对程序的极限条件检查还是很到位的。
作业分析
Unit3要求我们模拟现实生活中的社交网络,迭代主要是增加不同的功能。本单元的作业分析有独特的地方——因为三次作业都采用了一致的架构,因此以第三次作业的UML图为例即可了解整个单元迭代开发的过程。

从UML图可以很直接地看出来整个系统的模拟思路:通过分包策略,让网络(graph)、人(vertex)、算法(algo)之间实现“透明调度”。如此清晰的架构得益于课程组的精心设计,让我真真切切感受到了JML规范在Java语言中的强大。
这里说一点题外话,今天在知乎上看到一个问题:
如果让你来创建一种全新的语言,你会希望它具有什么特征?
高赞回答中提到了
能够通过一种程序员和用户都能看懂的格式抽象出所有运行流程的逻辑语义。
这不就是JML最擅长的吗?这样看来,JML早已悄悄地跑到了大多数语言前面。
第一次作业
第一次作业要求我们实现一个支持可达性判断的图模拟社交网络。
UML图如下

- 代码结构
结构大致可以分为三层:graph->vertex->algo.在算法层面不用考虑每个人具体的情况,只需要知道这是个点,并且图是无向的,那么就可以在接口一致的情况下调用封装好的算法了。(这和Java自带的数据结构非常相似)
- 复杂度分析


从反馈结果可以看出,因为课程组为我们提供的良好架构,因此复杂度都体现在算法层面,而不是系统本身的逻辑架构上。这么说或许有些抽象,进一步阐释就是说,复杂度的升高并不是因为架构的复杂,而是来自算法对逻辑和数据结构要求的提高。
第二次作业
第二次作业要求我们实现可以增加子图,并在子图中进行查询的社交网络模拟系统。
UML图如下

- 代码结构
代码架构基本延续了第一次的设计风格,虽然加入了SocialGroup这个子图结构,但是可以发现UML图的三层结构几乎没有变化。
- 复杂度分析


从反馈结果可以看出,这次的复杂度依然集中体现在算法中,而不是架构上,JML的威力足够让人吃惊。
第三次作业
第三次作业要求我们进一步实现可以进行高级查询操作的社交网络模拟系统。
UML图如下

- 代码结构
代码架构基本延续了第一次的设计风格,但是出于查询要求需要,增加了一些新的算法。
- 复杂度分析


从反馈结果可以看到,由于queryStrongLinked的出现,导致了最高复杂度的“易主”,因为采用的是Menger定理的枚举思想,因此循环较多,在圈复杂度上体现得尤为明显。
评测相关
重难点聚焦
从三次开发和互测的过程来看,问题和上一单元相同,主要都在CPU时间上。但是这一单元出现这个问题的原因不是来自大家的陌生,就我自己而言,是对某些算法的陌生和复杂度的估计不准确。
在第三次作业中,因为是根据Menger定理枚举获得分离集进行queryStrongLinked的判断,虽然采用了数组和BFS代替普通的数据结构和递归进行加速,但是还是出现了超时的情况。讲道理复杂度和标程的\(O(n^2)\)相比应该是会小一点的,可能因为标程在其他的部分做的更加完善,所以能够不超时。
之后将算法改为了Tarjan来进行判断,时间复杂度确实下降不少。
权当这是个教训吧,还是要时刻牢记:
要射下太阳的人,他的箭头总会比太阳高一些的。
Hack所用的策略
主要有两种方式:
- 使用完成作业时有意义的样例进行测试
- 利用评测机自己生成数据
评测机简要介绍
这是评测机的working directory

center:存放评测的核心控制代码,用于组织编译->运行->反馈功能data:存放自动生成的数据download_data:存放测试中出现问题的数据,可以用于回归测试。factory:存放数据生成代码output:存放各个测试代码的输出result:存放各个测试代码的结果ruler:存放标程src:存放源码filter.py:用于格式化数据以提交
重构策略
本单元的一大特点就是规格化编程,同时得益于课程组的精心设计,在架构上不需要我们进行过多的自主设计。课程组已经为我们设计好了相当完备和精巧的架构,因此本单元的重构问题其实是被巧妙地规避了,在这里衷心地向老师和助教们的辛苦构思表示感谢。
课程体验感受
这一单元的迭代开发让我接触到了Java工程的超现实主义开发方法:面向JML规格编程。请原谅我给它取了一个略显中二的名字,但是它的与众不同的确让人无法抗拒。
我们很多时候都会面临读别人写的代码的情况——无论是在此基础上继续开发,还是借鉴思想另起炉灶,对代码执行逻辑的准确理解都是相当重要的。因此,养成良好的阅读习惯不仅有助于我们去理解别人,更有助于我们规范自己的风格。这一单元的学习在我看来是非常有必要的,因为它让大多数人第一次不是仅仅为了完成功能而写代码,而是开始为了搭建一个系统并构建一个良好的可扩展的生态,这是让我醉心于OO的一大原因,也是OO的魅力所在。
最后,再次感谢一路上陪伴的老师、助教、同学,谢谢大家!
Unit3-窝窝社交圈的更多相关文章
- HipHop算法:利用微博互动关系挖掘社交圈
/* 版权声明:可以任意转载,转载时请务必标明文章原始出处和作者信息 .*/ CopyMiddle: 张俊林 TimeStamp:2012年3 月 在微博环境下,如何 ...
- 百度大脑UNIT3.0智能对话技术全面解析
智能客服.智能家居.智能助手.智能车机.智能政务……赋予产品智能对话能力是提升产品智能化体验.高效服务的重要手段,已经开始被越来越多的企业关注并布局.然而,智能对话系统搭建涉及NLP.知识图谱.语音等 ...
- 百度大脑UNIT3.0详解之嵌入式对话理解技术
相信很多人都体验过手机没有网时的焦虑,没有网什么也做不了.而机器人也会遇到这样的时刻,没有网或者网络环境不好的情况下,无法识别用户在说什么,也无法回复用户.在AIoT(AI+物联网)飞速普及的现在,智 ...
- 百度大脑UNIT3.0详解之知识图谱与对话
如今,越来越多的企业想要在电商客服.法律顾问等领域做一套包含行业知识的智能对话系统,而行业或领域知识的积累.构建.抽取等工作对于企业来说是个不小的难题,百度大脑UNIT3.0推出「我的知识」版块专门为 ...
- 百度大脑UNIT3.0详解之数据生产工具DataKit
在智能对话项目搭建的过程中,高效筛选.处理对话日志并将其转化为新的训练数据,是对话系统效果持续提升的重要环节,也是当前开发者面临的难题之一.为此百度大脑UNIT推出学习反馈闭环机制,提供数据获取.辅助 ...
- 百度大脑UNIT3.0解读之对话式文档问答——上传文档获取对话能力
在日常生活中,用户会经常碰到很多复杂的规章制度.规则条款.比如:乘坐飞机时,能不能带宠物上飞机,3岁小朋友是否需要买票等.在工作中,也会面对公司多样的规定制度和报销政策.比如:商业保险理赔需要什么材料 ...
- 百度大脑UNIT3.0详解之语音语义一体化方案
在电话客服场景里,用户和机器人交流的过程中,经常会出现沉默.打断机器人.噪声等情况,机器人在应对这些异常情况的时候,需要语音和语义理解技术进行处理,才能实现用户和机器人的流畅交谈.而这些能力的获取与应 ...
- JML规格编程系列——OO Unit3分析和总结
本文是BUAA OO课程Unit3在课程讲授.三次作业完成.自测和互测时发现的问题,以及倾听别人的思路分享所引起个人的一些思考的总结性博客.主要包含JML相关梳理.SMT Solver验证.JML单元 ...
- LuoguP5464 缩小社交圈
LuoguP5464 缩小社交圈 背景:洛谷七月月赛T4 题目大意给定\(n\)个点,每个点的权值对应着一个区间\([l_i,r_i]\),两个点\(i,j\)有边当且仅当他们权值的并集不为空集,问有 ...
随机推荐
- table动态添加的tr 其click事件在IE兼容模式中不执行 jquery 1.9 的live事件 和获取 first last
http://www.css88.com/jqapi-1.9/first-of-type/index.html#p=//www.css88.com/jqapi-1.9/last-child-selec ...
- JSP知识点回顾
- 为什么说OC是运行时语言?什么是动态类型、动态绑定、动态加载?
转载:https://www.cnblogs.com/dxb123456/p/5525343.html 动态: 主要是将数据类型的确定由编译时,推迟到了运行时. 这个问题其实浅涉及到两个概念,运行时和 ...
- Write a merge sort program
Merge Sort- Recursion Write a merge sort program in JavaScript. Sample array : [34, 7, 23, 32, 5, 62 ...
- web项目——javax.servlet.ServletException: Circular view path [registerForm]
报错: 控制台输出: 三月 21, 2019 10:12:32 上午 org.springframework.web.servlet.PageNotFound noHandlerFound 警告: N ...
- 自定义realm实现授权
对用户和权限封装 更具用户查询已经拥有的角色 实现类 根据用户查询已经拥有的权限
- PHP 连接数据库基础操作
<?phpheader('Content-type:text/html;charset=utf-8');//1建立 或者 关闭mysql服务器 @符号用于屏蔽错误信息$link=@mysql ...
- Maven快速入门(一)Maven介绍及环境搭建
做开发的程序员都知道,在系统开发需要各自各样的框架.工具.其中有一种工具不管你是初级程序员还是高级程序员都必须熟练掌握的,那就是项目管理工具(maven.ant.gradle).接下来就总结Maven ...
- 没找到工作的Java软件工程师是屌丝中的屌丝啊
Java软件开发的工作咋就那么难找呢?
- [COCOS2DX-LUA]0-004.cocos2dx中的DrawNode的init的方法问题
1.诱因 近期,项目接入了Bugly, 上报了一些平常测试不出来,或者很难重现的bug,这类bug非常难排查.原因有二,第一,问题无法重现,第二,修改了无法立即验证结果.有一个问题困恼了我很久,就是一 ...