zoukankan      html  css  js  c++  java
  • OO第三单元总结

    一、理论基础

    1.什么是JML

    JML,即Java Modeling Language,是用于对Java程序进行规格化设计的一种表示语言。JML是一种行为接口 、规格语言(Behavior Interface Specification Language,BISL),基于Larch方法构建。BISL提供了对方法和类型的规格定义手段。所谓接口即一个方法或类型外部可见的内容。通过JML及其支持工具,不仅可以基于规格自动构造测试用例,并整合了SMT Solver等工具以静态方式来检查代码实现对规格的满足情况。

    2.注释结构

    JML的注释主要有行注释和块注释两种,用于不同情景

    行注释的表示方式为: //@annotation

    块注释的方式为: /*@annotation@*/

    3.常用表达式

    esult表达式:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。

    old( expr )表达式:用来表示一个表达式 expr 在相应方法执行前的取值。

    ot_assigned(x,y,...)表达式:用来表示括号中的变量是否在方法执行过程中被赋值。

    forall表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。

    exists表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。

    sum表达式:返回给定范围内的表达式的和。

    max表达式:返回给定范围内的表达式的最大值。 

    min表达式:返回给定范围内的表达式的最小值。 

    um_of表达式:返回指定变量中满足相应条件的取值个数。

    4.方法规格

    方法规格的核心内容包括三个方面,前置条件、后置条件和副作用约定。

    前置条件是对方法输入参数的限制,如果不满足前置条件,方法执行结果不可预测,或者说不保证方法执行结果的正确性。前置条件通过requires子句来表示: requires P;。

    后置条件是对方法执行结果的限制,如果执行结果满足后置条件,则表示方法执行正确,否则执行错误。后置条件通过ensures子句来表示: ensures P;。

    副作用指方法在执行过程中对输入对象或this对象进行了修改(对其成员变量进行了赋值,或者调用其修改方法)。JML提供了副作用约束子句,使用关键词 assignable 或者 modifiable 。

    5.数据规格

    JML针对类型规格定义了多种限制规则,目前主要涉及两类,不变式限制(invariant)和 约束限制(constraints)。无论哪一种,类型规格都是针对类型中定义的数据成员所定义的限制规则,一旦违反限制规则,就称相应的状态有错。

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

    对象的状态在变化时往往也许满足一些约束,这种约束本质上也是一种不变式。JML为了简化使用规 则,规定invariant只针对可见状态(即当下可见状态)的取值进行约束,而是用constraint来对前序可见 状态和当前可见状态的关系进行约束。

    二、SMT solver与JMLUnit测试

    可能是由于版本的原因,这个我一直没能成功,命令行调用openJML时会得到如下报错:

    在把jmlunitng.jar复制到src文件夹后,执行如下四条命令

      java -jar jmlunitng.jar test/MyGroup.java

      javac -cp jmlunitng.jar test/*.java

      java -jar openjml.jar -rac test/MyPerson.java test/MyGroup.java

      java -cp jmlunitng.jar test.MyGroup_JML_Test

    然而在执行到第二步的时候发生了如下报错:

    三、作业架构设计

    1.第一次作业

    类图

    emm由于直接没进互测,找不到当时的代码了,生成不了类图,不过我还是可以简单的复述一下自己的架构的,毕竟第一次作业非常简单(简单到没进互测...)。当时我本来给这次作业预留了和前六次作业差不多的时间完成,没想到一上手,呦,这么简单的吗,这不就照着规格写嘛。于是我沉浸在这意外的喜悦中,也没有认真的思考应该采用什么容器,通通使用了Arraylist,这使得我后一次作业一开始的性能极差,同时,我也没有特别仔细的看官方包的规格,直接导致了我这一次没进互测。

    2.第二次作业

    类图

    第二次作业增加了一个group类,同时还有时间限制,这一次我吸取了上次没进互测的惨痛教训,写每一个方法前都认真的读一遍规格,过了中测之后,我长呼一口气,认为这次应该没有什么问题了。诶,对了,这次好像还有时间限制,我自己生成了一组比较极端的数据,跑了一遍,然后我的IDEA直接卡炸了...必须开任务管理器清那种,于是又开始修复性能...最终,我的Network里的Person用Arraylist和Hashmap两个容器各存一遍,遍历用Arraylist,查找用Hashmap,IsCircle方法也使用了查并集,而Group里的方法我是通过设置有效位来避免重复计算,addperson之后5个有效位都变false,addrelation之后前两个有效位变false,最终终于过了强测,却还是被组里的大佬hack出了tle...这点到后面的bug修复再讨论。

    3.第三次作业

    类图

    第三次的作业增加了组内删人的操作,同时在Network里增加了几个方法,也就是这几个方法成为了第三单元最大的难点(个人认为),关于新增的Money系统,相关的方法不多,除了新写的借钱和查钱,只需要在Network里加一个Hashmap来存储每个人的钱,同时在addperson的时候记得初始化出来就好。至于blocksum,和第二次作业中group里面的五个属性类似,选择动态维护。最关键的qmp和qsl两个函数,qsl我借鉴讨论区的大佬,采用了bsf+分别删去路径上每一个结点检查是否联通的方法,而qmp我自己想了一个方法,那就是每次addrelation的时候就更新每个点到每个点的最短路径,这样一来每次调用这个方法就只用查表就好了,简直完美!但是这个方法在性能上也有大问题,最终导致了我又一次没进互测...这个悲伤的故事也留到bug修复那里再谈。

    四、BUG及修复

    1.第一次作业

    第一次作业的bug是在addrelation方法,我在查看规格的时候漏掉了一行,没有看到id1==id2要当无事发生,我直接当异常了,于是一个悲伤的故事就发生了...

    2.第二次作业

    第二次作业没有在强测中测出bug,但是同屋的大佬测出了我的性能还是不过关,尤其是group类里的五个统计量,虽然我每次设置了有效位,但是当有效位无效时,我还是要从头开始一个一个算,这就导致了前面的人的属性被重复的计算了,造成了性能上的浪费,最终我再一次改进,改成每次在addtogroup的时候就更新几个统计值,包括年龄和、年龄的平方的和等,再由这些数比较方便的求出想要求出的数值,如方差,性能得到了进一步的提升。

    3.第三次作业

    第三次作业仍然没有进互测...主要有三个bug,第一个是关于blocksum的,主要原因是把更新是的判断条件islcircle写成了islinked,我也不知道当时为啥要这么写,大概脑子短路了吧...然后就是再qsl中我有一个辅助debug的print没注释掉,似乎中测也没测到这个方法,然后强测就悲剧了;还有就是之前说过的qmp,我选择在addrelation的时候更新每个人到每个人的最短距离,看似qmp的时候很美好,一下就出来了,但是在addrelation的时候太慢了啊,而且addrelation执行得到频率可比qmp要多,以强测中的一条数据为例,3000条指令只有addperson和addrelation,我居然跑了七十多秒。。。最后还是老老实实改了个堆优化的Dijkstra。

    五、心得体会

    短短三次作业,我两次没进互测,内心是崩溃的......

    崩溃完了,还得找找原因,照着JML规格写代码一定要仔细,认真理解其全部含义。规格最基本的一个特点就是准确,无二义,那么在阅读这样精确的语言时,一个疏忽就可能酿成大错,造成严重后果,比如没进互测...另外就是,在写那些考验性能的算法时,如果想采用什么奇技淫巧,一定要先算好复杂度,看看是不是真的很简单,就算采用了一些经典的算法,也可以自己试着写一写复杂度,也可以加深对代码的理解,总之不会有坏处。

    这个单元主要学习了JML语言规格,做作业的时候感觉也和前两次不大一样,前两次其实没有怎么考验性能(性能分占比不高),以正确性优先。而这个单元里,尤其时前两次作业,做到正确并不难,但是性能却成为了影响分数的重要因素。这也提醒了我们,规格只是规格,和实现没有关系,在具体实现上不能一点脑子不动,若是看规格给了个数组,那就也声明一个数组,正确性固然是问题不大了,但是和别人真正动了脑子想架构的相比,性能就差了十万八千里了。

    马上进入OO最后一个单元了,这个学期也马上进入尾声了,希望自己在接下来的时间要更加努力,也给自己交一个满意的答卷。

  • 相关阅读:
    记一道乘法&加法线段树(模版题)
    2021CCPC网络赛(重赛)题解
    Codeforces Round #747 (Div. 2)题解
    F. Mattress Run 题解
    Codeforces Round #744 (Div. 3) G题题解
    AtCoder Beginner Contest 220部分题(G,H)题解
    Educational Codeforces Round 114 (Rated for Div. 2)题解
    Codeforces Global Round 16题解
    Educational Codeforces Round 113 (Rated for Div. 2)题解
    AtCoder Beginner Contest 182 F
  • 原文地址:https://www.cnblogs.com/12138abc/p/12944588.html
Copyright © 2011-2022 走看看