oo第三单元总结

一、作业概览
本单元作业主要考察学生们对于JML规格的理解与实现,共分为三次作业。
第一次作业,要求实现MyPath与MyPathContainor。在这次作业中,主要实现的操作是对路径的线性管理,较为困难的函数是getdistincnode。原因是,在此函数上的复杂度如果没有进行一定的考量,会很容易炸掉。
第二次作业,要求实现MyPath与MyGrash。在这次作业中,主要实现的操作是,将第一次作业的管理上升至图型管理。较为困难的函数是getshortestPathLength,原因同上述。
第三次作业,要求在第二次作业的基础上,实现MyRailSystem.这次作业,主要实现的操作,是对图上升至实际问题的地铁路线的考虑。较为困难的函数,倒没有,在第二次作业的算法上进行衍生就可以很好的解决此次作业。

二、JML语言
JML(Java Modeling Language)是一种形式化的、面向Java的行为接口规格语言(Behavior Interface Specification Language,BISL),基于Larch方法构建。笔者对于JML的了解不是特别的深,主要对它的了解来源于课上提供的JML_level_0手册。在笔者的理解上,JML是帮助程序员理解程序功能实现的一种辅助工具,有其本身的语言撰写方式。
2.1 原子表达式
\result表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。\result表达式的类型就是
方法声明中定义的返回值类型。如针对方法: public boolean equals (Object o) ,\result的类型是 boolean ,
任意传递一个 Object 类型的对象来调用该方法,可以使用\result来表示 equals 的执行结果( true 表示 this 和 o
相等; false 表示不相等)。
\old( expr )表达式:用来表示一个表达式 expr 在相应方法执行前的取值。该表达式涉及到评估 expr 中的对象是否
发生变化,遵从Java的引用规则,即针对一个对象引用而言,只能判断引用本身是否发生变化,而不能判断引用所指
向的对象实体内容是否发生变化。假设一个类有属性 v 为 HashMap ,假设在方法执行前v的取值为 0x952ab340 ,即
指向了存储在该地址的具体 HashMap 对象,则\old( v )的值就是这个引用地址。如果方法执行过程中没有改变 v 指向
的对象,则 v 和old( v )有相同的取值,即便方法在执行过程中对 v 指向的 HashMap 执行了插入或删除操作。因此
v.size() 和\old( v ). size() 也有相同的结果。很多情况下,我们希望获得 v 在方法执行前所管理的对象个数,这
时应使用\old( v.size() )。作为一般规则,任何情况下,都应该使用\old把关心的表达式取值整体括起来。\not_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值,返回为
true ,否则返回 false 。实际上,该表达式主要用于后置条件的约束表示上,即限制一个方法的实现不能对列表中
的变量进行赋值。
\not_modifified(x,y,...)表达式:与上面的\not_assigned表达式类似,该表达式限制括号中的变量在方法执行期间的取
值未发生变化。
\nonnullelements( container )表达式:表示 container 对象中存储的对象不会有 null ,等价于下面的断言,其中
\forall是JML的关键词,表示针对所有 i 。
\type(type)表达式:返回类型type对应的类型(Class),如type( boolean )为Boolean.TYPE。TYPE是JML采用的缩略
表示,等同于Java中的 java.lang.Class 。
\typeof(expr)表达式:该表达式返回expr对应的准确类型。如\typeof( false )为Boolean.TYPE。
2.2 量化表达式
\forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。 (\forall int
i,j; 0 <= i && i < j && j < 10; a[i] < a[j]) ,意思是针对任意 0<=i,a[i]。这个表达式如果
为真( true ),则表明数组a实际是升序排列的数组。
\exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。 (\exists
int i; 0 <= i && i < 10; a[i] < 0) ,表示针对0<=i<10,至少存在一个a[i]<0。
\sum表达式:返回给定范围内的表达式的和。 (\sum int i; 0 <= i && i < 5; i) ,这个表达式的意思计算[0,5)
范围内的整数i的和,即0+1+2+3+410。注意中间的 0 <= i && i < 5 是对i范围的限制,求和表达式为最后面的
那个 i 。同理,我们构造表达式 (\sum int i; 0 <= i && i < 5; ii) ,则返回的结果为1+4+9+16。
\product表达式:返回给定范围内的表达式的连乘结果。 (\product int i; 0 < i && i < 5; i) ,这个表达式的
意思是针对(0,5)范围的整数的连乘结果,即1
2* 3 * 4
24。
\max表达式:返回给定范围内的表达式的最大值。 (\max int i; 0 <= i && i < 5; i) ,这个表达式返回[0,5)中
的最大的整数,即4。
\min表达式:返回给定范围内的表达式的最小值。 (\min int i; 0 <= i && i < 5; i) ,这个表达式返回[0,5)中
的最小的整数,即0。
\num_of表达式:返回指定变量中满足相应条件的取值个数。 (\num_of int x; 0;x%20) ,这个表
达式给出(0,20]以内能够被2整除的整数个数,得到的数目为10。一般的,\num_of表达式可以写成 (\num_of T x;
R(x);P(x)) ,其中T为变量x的类型,R(x)为x的取值范围;P(x)定义了x需要满足的约束条件。从逻辑上来看,该表达
式也等价于 (\sum T x;R(x)&&P(x);1) 。
2.3 集合表达式
集合构造表达式:可以在JML规格中构造一个局部的集合(容器),明确集合中可以包含的元素。 new
JMLObjectSet {Integer i | s.contains(i) && 0 < i.intValue() } 表示构造一个JMLObjectSet对象,其中
包含的元素类型为Integer,该集合中的所有元素都在容器集合s中出现(注:该容器集合指Java程序中构建的容器,
比如ArrayList),且整数值大于0。集合构造表达式的一般形式为:new ST {T x|R(x)&&P(x)},其中的R(x)对应集合
container != null &&
(\forall int i; 0 <= i && i < container.length;
container[i] != null)
1
2
3中x的范围,通常是来自于某个既有集合中的元素,如s.has(x),P(x)对应x取值的约束。
2.4 操作符
JML表达式中可以正常使用Java语言所定义的操作符,包括算术操作符、逻辑预算操作符等。此外,JML专门又定义
了如下四类操作符。
(1) 子类型关系操作符: E1<:E2 ,如果类型E1是类型E2的子类型(sub type),则该表达式的结果为真,否则为假。如果E1和E2是相同的类型,该表达式的结果也为真,如 Integer.TYPE<:Integer.TYPE 为真;但Integer.TYPE<:ArrayList.TYPE 为假。需要指出的是,任意一个类X,都必然满足 X.TYPE<:Object.TYPE 。
(2) 等价关系操作符: b_expr1<
>b_expr2 或者 b_expr1<=!=>b_expr2 ,其中b_expr1和b_expr2都是布尔表达式,这两个表达式的意思是 b_expr1b_expr2 或者 b_expr1!=b_expr2 。可以看出,这两个操作符和Java中的 和 != 具有相同的效果,按照JML语言定义, <> 比 == 的优先级要低,同样 <=!=> 比 != 的优先级低。
(3) 推理操作符: b_expr1
>b_expr2 或者 b_expr2<b_expr1 。对于表达式 b_expr1>b_expr2 而言,当b_expr1false ,或者 b_expr1true 且 b_expr2==true 时,整个表达式的值为 true 。
(4) 变量引用操作符:除了可以直接引用Java代码或者JML规格中定义的变量外,JML还提供了几个概括性的关键词来引用相关的变量。\nothing指示一个空集;\everything指示一个全集,即包括当前作用域下能够访问到的所变量。变量引用操作符经常在assignable句子中使用,如 assignable \nothing 表示当前作用域下每个变量都不可以在方法执行过程中被赋值。
三、OpenJML和JMLUnitNG的使用
接下来通过检察减法运算来进行测试

使用openjml进行源编码
生成的目录如下

使用javac进行编译并得出结果

由此可知,发出了溢出结果。
四、架构设计
第一次作业

在本次作业中,主要涉及到关于PATH和Container的设计。为了尽可能的降低复杂度。笔者采用的是三个哈希表。hash1储存<Path,ID>,hash2储存<ID,path>,hash3存储<element,number>,在每次add与remove操作时,对三个hash进行更新,进而使得其他的查询功能性操作的复杂度降低。
第二次作业

在第二次作业中,主要涉及到Grash的涉及。对于第一次作业中的操作,笔者直接把第一次的copy过来了。对于图的存储,笔者用的是哈希表然后对于图的遍历,笔者选择的方法是弗洛伊德算法,当每次add与remove操作时,进行一边图的更新,把最短路径用数组保存起来,进而在后面的查询中就可以直接出现,从而降低复杂度。
第三次作业

在第三次作业中,主要是实现地铁路线的查询。在这次作业中,对于算法的要求不高,主要在想。对于票价,不满意度,的查询,笔者通过查看讨论区的一位大佬的思路,成功带入自己的第二次算法,同上在每次add与remove操作时就遍历一遍,然后存起来。

五、bug分析
在第一次中互测没有bug,但是强测出现了CPU超时,主要还是因为复杂度过高,后期修改了算法的复杂度,成功通过。
第二次作业的bug同上。
第三次作业中没有bug,主要吸取了前两次的经验,然后在之前就优化了算法。
JML规格设计作业整体来讲还是比前两个单元好做一点,接下来一个单元也会加油的。

posted @ 2019-05-22 17:13  皮卡丘爱吃橙子  阅读(231)  评论(0编辑  收藏  举报