JML这一单元是真的有含金量,很有难度。而且这难点和前两单元完全不同,前两单元是容易架构混乱导致细节出问题,JML单元是读不懂JML规格的话架构都构不出来,以及即使能够读懂JML规格了,让自己写规格的时候又蒙圈了(大哭.jpg)。以及部署JML相关工具的时候能够比以上都令人崩溃(暴风哭泣.jpg)。

一、JML语言及工具

JML语言

JML(java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。其利用前置条件、后置条件、不变式等约束语法,描述了Java程序的数据、方法和类的规格,是一种契约式程序设计的实现工具。

1.原子表达式

\result: 方法(非void类型)执行后的返回值。

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

2.量化表达式

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

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

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

3.操作符

<==>, <=!=>: 等价关系操作符

== >,< ==: 推理操作符

\nothing, \everything: 变量引用操作符

4.方法规格

requires: 前置条件

ensures: 后置条件

assignable, modifiable, pure: 副作用范围限定

signals, signals_only: 异常抛出语句

5.类型规格

invariant: 不变式

constraint: 变化约束

以上仅是对JML最简单且最常用语法等的梳理。其次,仅仅知晓这些基础语法是不够的,一定要多看代码,多书写JML规格进行练习(否则就会在要求写JML的时候大翻车)。

工具部分
  • OpenJML: 对JML进行规范性检查
  • JMLUnitNg: 结合OpenJml可实现对代码的自动化测试。

二、SMT Solver验证

对于OpenJml的部署,参考了学长的这篇博客https://www.cnblogs.com/lutingwang/p/openjml_basic.html(可以不用添加环境变量,直接命令行到对应文件夹下运行openjml命令即可)。下面是我的测试经过……

略过出现这个结果的各种报错,终于算是弄出来一个稍微能看的结果,此处报错显示Person类的缺失,然后我放入Person类进行测试……

(上图*.java仅包含Person类和Group类,均为官方第三次作业接口代码)然后出现了这个错,然后表示看不懂以及弄不懂了之后,遂放弃使用openjml进行进一步测试。

三、JMLUnitNg测试

此处工具的部署参考J哥的博客https://www.cnblogs.com/pekopekopeko/p/12920417.html

在其流程上修改为对自己的MyGroup类的测试(将Group的规格扔进了MyGroup类),同时魔改了Person类避免各种恼人的报错。

上图为成功运行时文件内容。

C:\xxxxxxx\jml>java -cp jmlunitng.jar test.MyGroup_JML_Test
[TestNG] Running:
Command line suite Failed: racEnabled()
Passed: constructor MyGroup(-2147483648)
Passed: constructor MyGroup(0)
Passed: constructor MyGroup(2147483647)
Failed: <<test.MyGroup@6842775d>>.addPerson(null)
Passed: <<test.MyGroup@6c629d6e>>.addRelation(-2147483648, -2147483648, -2147483648)
Passed: <<test.MyGroup@27abe2cd>>.addRelation(0, -2147483648, -2147483648)
Passed: <<test.MyGroup@51016012>>.addRelation(2147483647, -2147483648, -2147483648)
Passed: <<test.MyGroup@1517365b>>.addRelation(-2147483648, 0, -2147483648)
Passed: <<test.MyGroup@60215eee>>.addRelation(0, 0, -2147483648)
Passed: <<test.MyGroup@768debd>>.addRelation(2147483647, 0, -2147483648)
Passed: <<test.MyGroup@449b2d27>>.addRelation(-2147483648, 2147483647, -2147483648)
Passed: <<test.MyGroup@66133adc>>.addRelation(0, 2147483647, -2147483648)
Passed: <<test.MyGroup@24273305>>.addRelation(2147483647, 2147483647, -2147483648)
Passed: <<test.MyGroup@1c2c22f3>>.addRelation(-2147483648, -2147483648, 0)
Passed: <<test.MyGroup@5a42bbf4>>.addRelation(0, -2147483648, 0)
Passed: <<test.MyGroup@4f4a7090>>.addRelation(2147483647, -2147483648, 0)
Passed: <<test.MyGroup@769c9116>>.addRelation(-2147483648, 0, 0)
Passed: <<test.MyGroup@12bc6874>>.addRelation(0, 0, 0)
Passed: <<test.MyGroup@1ef7fe8e>>.addRelation(2147483647, 0, 0)
Passed: <<test.MyGroup@5d3411d>>.addRelation(-2147483648, 2147483647, 0)
Passed: <<test.MyGroup@6979e8cb>>.addRelation(0, 2147483647, 0)
Passed: <<test.MyGroup@2be94b0f>>.addRelation(2147483647, 2147483647, 0)
Passed: <<test.MyGroup@50675690>>.addRelation(-2147483648, -2147483648, 2147483647)
Passed: <<test.MyGroup@47d384ee>>.addRelation(0, -2147483648, 2147483647)
Passed: <<test.MyGroup@3930015a>>.addRelation(2147483647, -2147483648, 2147483647)
Passed: <<test.MyGroup@1ff8b8f>>.addRelation(-2147483648, 0, 2147483647)
Passed: <<test.MyGroup@c39f790>>.addRelation(0, 0, 2147483647)
Passed: <<test.MyGroup@5f150435>>.addRelation(2147483647, 0, 2147483647)
Passed: <<test.MyGroup@75412c2f>>.addRelation(-2147483648, 2147483647, 2147483647)
Passed: <<test.MyGroup@f5f2bb7>>.addRelation(0, 2147483647, 2147483647)
Passed: <<test.MyGroup@3ecf72fd>>.addRelation(2147483647, 2147483647, 2147483647)
Failed: <<test.MyGroup@77f03bb1>>.delPerson(null)
Passed: <<test.MyGroup@7a92922>>.equals(null)
Passed: <<test.MyGroup@5474c6c>>.equals(java.lang.Object@4b6995df)
Passed: <<test.MyGroup@445b84c0>>.getAgeMean()
Passed: <<test.MyGroup@63d4e2ba>>.getAgeVar()
Passed: <<test.MyGroup@7006c658>>.getConflictSum()
Passed: <<test.MyGroup@7cdbc5d3>>.getId()
Passed: <<test.MyGroup@3834d63f>>.getPeopleSum()
Passed: <<test.MyGroup@34340fab>>.getRelationSum()
Passed: <<test.MyGroup@3ab39c39>>.getValueSum()
Failed: <<test.MyGroup@546a03af>>.hasPerson(null) ===============================================
Command line suite
Total tests run: 121, Failures: 10, Skips: 0
===============================================

上图为MyGroup类的实际测试效果(已删除部分测试代码)。由上图具体测试数据分析可知,JMLUnitNg测试数据几乎全部都是边界数据、0和null对象,感觉这样的测试意义也不太大,不具有普适性,不能起到很好的效果。

使用上面所述的工具的整体感受就是,这些工具真的很不完善,以及太鸡肋了,感觉意义不大。

四、作业架构分析

从总结课上知晓了这次作业的实际意图是让我们抽象出图结构,明晰代码的层次架构,从而更好的理解面向对象的思想。但是emmmmmm感觉自己的大多数时候其实是照着规格写代码,虽然抽象出了一定的图结构从而使用一定的图算法来优化,但是感觉还是离这一单元真正的目标有点差距。

三次作业的迭代关系和递进关系很明显,Person类即为一个节点,relation为边,在整个network里,将所有的person抽象为一幅图,在图中进行操作。Group类似于network的子图,其拥有完全不同于network的功能。

第一次作业:

第一次作业,基本按照规格实现,难点在于对规格的理解和对细节的掌握。在将架构抽象为图之后即可按照图的算法来解决iscircle问题,在此我选用的bfs算法,虽然不会超时,但是还是没有以后作业用的并查集香。

第二次作业

第二次作业增加了Group类,并且对于network类增加和Group类交互的功能,对于新增功能的实现都是按部就班的根据规格实现即可,但是由于测试数据集的增加,就要求我们在实现的时候要注意效率。所以我在Group内缓存了group类需求的所有数据,极大的节省了时间。其实感觉group类更像是节点的集合,并且维护集合内节点的共同属性。

第三次作业

第三次作业,对group新增del_person方法,对netwerk类新增更多的方法。其中group类的新增方法要求我们对缓存数据的更新需要更加小心,否则会wa的很惨。其次,network的新增方法抽象来看就是求连通块数目,求最短路径,判断两点是否强联通,这些都是图论的算法,这些方法的增加就要求了我们对这一单元作业的架构的抽象和理解。在理解了架构之后基本按照图论算法按部就班的写代码即可。

对于连通块数目的判断,我采用的并查集方式,同时也更新了iscircle方法,进一步节省了时间。

求最短路径采用了堆优化的迪杰斯特拉算法。

两点强联通我开始用的是暴力删点进行bfs判断联通的方法,但是中测结束前和同学对拍被暴打之后,遂花费一天的时间研究了一波tarjan算法,极大的缩短了时间(但是tarjan算法真的太容易错了,不然我也不会花费一天的时间了),虽然最后助教说暴力解法能过,但是至少多收获了一种算法。

第三次作业本质是大一数据结构的复(huan)习(zhai)作业。(bushi

五、代码实现的bug

在三次强测和互测中均未出现问题。

由于这一单元作业我强迫自己缓慢并且认真读规格,所以除开第三次算法部分,其余都是一次性过关,但是在和室友对拍的时候,室友总会冒出各种各样的小细节问题,同时也顺便给自己重新检查了一次,以及对作业的坑点有了进一步的了解。(虽然和室友对拍了很多,但是室友好像偶尔还是在强测或者互测出点问题,遗憾没能大家一起满分)

在前两次的互测中,由于屋内都是大佬,所以在测试完几个易错点和部分朴实数据之后就放弃了。当然最终结果还是屋内一直安静到底。但是在第三次互测中,由于自己摸了点鱼,评测机的数据针对性不强,所有在大部分人都hack了一个点的情况下,我的评测机毫无收获(我和我的评测机都太菜了)。

六、心得体会

虽然仅仅接触JML了三四周,但是确实也感受了契约式编程的优点和魅力,以及写的好的JML的无歧义是真的比普通的语言描述简洁而且舒服多了。但是JML也是真的难写(手动捂脸),非常容易描述不全,描述不清楚,和对情况的考虑的不完整……考虑的事情和细节太多太杂了,此处感谢此单元助教的辛苦付出(

其次,这一单元帮我复习了很多了图论的算法,迪杰斯特拉算法、bfs、dfs还新学了tarjan算法,收获颇丰。同时对算法的复习,也让我对程序性能有了更进一步的理解。总的来说这一单元的体验是真的不错,此处再次感谢老师和助教和付出。

OO_Unit3_Summary的更多相关文章

随机推荐

  1. 翻译:《实用的Python编程》02_02_Containers

    目录 | 上一节 (2.1 数据类型) | 下一节 (2.3 格式化) 2.2 容器 本节讨论列表(list),字典(dict)和集合(set). 概述 通常,程序必须处理许多对象. 股票的投资组合 ...

  2. 开源OA办公系统的“应用市场”,能够为协同办公开拓什么样的“前路”?

    在我们的日常生活中,应用市场这个词,总是与智能手机划上等号,不管使用的是iPhone还是安卓,总会接触到手机上的APP应用市场,我们可以在应用市场中,选择自己所需要的APP应用软件,下载使用后,可以让 ...

  3. React高级

    1.React应用 1.1创建应用 创建项目可以使用react脚手架,创建步骤如下 1)安装react脚手架 npm i -g create-react-app 2)创建项目 create-react ...

  4. Bitter.NotifyOpenPaltform : HTTP 异步消息接收调度中心&mdash;开源贡献 之 一:简介

    现在互联网的系统越来越趋向于复杂,从单体系统到现在的微服务体系演变.公司与公司的分工也越来越明确. 大数据公司提供了大数据服务 人脸识别公司提供了人脸识别服务 OCR 公司提供了专业的OCR 服务 车 ...

  5. arch 安装笔记

    arch- 第一次装archLinux时,照着别人的安装教程来安装,由于不懂有些命令的意思,装了好几次才成功,这次趁着热乎,把安装的步骤写下来,为自己踩踩坑(桌面是xfce,下面也有换桌面的方法,我第 ...

  6. k8s自定义controller设计与实现

    k8s自定义controller设计与实现 创建CRD 登录可以执行kubectl命令的机器,创建student.yaml apiVersion: apiextensions.k8s.io/v1bet ...

  7. (三)String、StringBuilder、StringBuffer在字符串操作中的性能差异浅析

    参考资料:https://www.iteye.com/blog/hank4ever-581463 <Core Java Volume I-Fundamentals>原书第十版 <Ja ...

  8. PAT-1147(Heaps)最大堆和最小堆的判断+构建树

    Heaps PAT-1147 #include<iostream> #include<cstring> #include<string> #include<a ...

  9. Pytorch1.7报错 Output 0 of UnbindBackward is a view and is being modified inplace

    utils里内容改成 if scale_each is True: for idx, _ in enumerate([jj for jj in tensor]): t = tensor[idx] # ...

  10. MongoDB -> kafka 高性能实时同步(sync 采集)mongodb数据到kafka解决方案

    写这篇博客的目的 让更多的人了解 阿里开源的MongoShake可以很好满足mongodb到kafka高性能高可用实时同步需求(项目地址:https://github.com/alibaba/Mong ...