2021年春季学期计算学部《软件构造》课程Lab 2实验报告

2024-01-02 07:50

本文主要是介绍2021年春季学期计算学部《软件构造》课程Lab 2实验报告,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1实验目标概述

本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象
编程(OOP)技术实现 ADT。具体来说:
⚫ 针对给定的应用问题,从问题描述中识别所需的 ADT;
⚫ 设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
⚫ 根据 ADT 的规约设计测试用例;
⚫ ADT 的泛型化;
⚫ 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示
(representation)、表示不变性(rep invariant)、抽象过程(abstraction
function)
⚫ 使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表
示泄露(rep exposure);
⚫ 测试 ADT 的实现并评估测试的覆盖度;
⚫ 使用 ADT 及其实现,为应用问题开发程序;
⚫ 在测试代码中,能够写出 testing strategy

2实验环境配置

在之前的基础上安装配置 EclEmma,https://www.eclemma.org/有教程,主要过程如下:

  1. From your Eclipse menu select Help → Eclipse Marketplace.
  2. Search for “EclEmma”.
  3. Hit Install for the entry “EclEmma Java Code Coverage”.
  4. Follow the steps in the installation wizard.
    安装完成
    在这里插入图片描述

在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab2-1190202002

3实验过程

3.1Poetic Walks

一.先构建一个有向加权图的操作,包括创建图,以及对图中顶点和边的增,删,改,查。
二.复用之前完成的图操作,将一段文本的各个词作为点,各词之间的联系作为边的权。即:此b出现在a后几次,则a到b的边权为几。
三.根据输入语段和完成的文本图,查找文本图中同时出现在输入语段两词a b,a后b前的词。将此词加入a b之间,以此类推,完成对输入语段的扩句。

3.1.1Get the code and prepare Git repository

如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。在 Get the code 步骤中,无法连接 MIT 的 Athena 服务器,将从以下地
址获取初始代码https://github.com/rainywang/Spring2021_HITCS_SC_Lab2/tree/master/P1

git clone https://github.com/rainywang/Spring2021_HITCS_SC_Lab2/tree/master/P1

git init 创建本地空仓库
git add README.md //文件添加到仓库
git add . //不但可以跟单一文件,还可以跟通配符,更可以跟目录。一个点就把当前目录下所有未追踪的文件全部add了
git commit -m “first commit” //把文件提交到仓库
git remote add origin git@github.com:wangjiax9/practice.git //关联远程仓库
git push -u origin master //把本地库的所有内容推送到远程库上

在作业描述中若遇到“commit and push”的要求,请将你的代码 push 到你
的 GitHub Lab2 仓库中。g

3.1.2Problem 1: Test Graph

设计:向GraphStaticTest.java和GraphInstanceTest添加测试。包括对add,set,remove,vertices,sources,targets的测试。
实现:静态 Graph.empty() 方法的测试策略是静态的,因此只有一种实现,我们只需要运行一次这些测试。MIT提供了这些测试。需要为 GraphInstanceTest.java 中的所有实例方法编写测试策略和测试。在这些测试中,必须使用 emptyInstance() 方法来获取新的空图,而不是 Graph.empty()。
过程:testAdd:添加点,测试图中是否包含这些点。
testSet:设置两点之间权值,测试当weight为正数,负数,0时,所得的之前weight的结果是否符合要求。
testRemove:删除点和相连的边,根据删除返回true,没有此点返回false,进行测试返回值与实际是否相同。并在删除后添加此点和相连的边,根据返回值判断是否真的删除。
testVertices:测试生成的点集是否将所有点包含在内。
testSources:测试根据source生成的Hashmap<targets,weights>与我们的判断应该生成的Map是否相同。
testTargets:测试根据target生成的Hashmap<sources,weights>与我们的判断应该生成的Map是否相同。
结果:全部完成后的测试结果,对graph代码覆盖率达到90.5%。
在这里插入图片描述
在这里插入图片描述

3.1.3Problem 2: Implement Graph

设计:实现分为两种,Implement ConcreteEdgesGraph和Implement ConcreteVerticesGraph。无论哪种,最终都需要实现图的add,set,remove,vertices,sources,targets函数。不同的是,对于两种实现,所用到的类和类中的操作不同,第一个用Edge实现,第二个用Vertices实现。导致图的函数实现略有不同。

3.1.3.1Implement ConcreteEdgesGraph

实现:由于是图的边实现,所以需要定义Edge类,在类中有边的出发点,终点,权值。类中应包含返回出发点,终点,权值的函数。然后用由所有Edge组成的列表和包含所有点的元组完成图的实现的各类函数add,set,remove,vertices,sources,targets。
过程:先完成Edge类的实现,边需要在类中定义私有变量出发点source,终点target,权值weight,根据这三个变量的输入构成Edge。
在Edge类中实现返回出发点source,终点target,权值weight的函数,直接返回对应的变量即可。完成toString函数,将边以字符串形式显示出来,直接将出发点source,终点target,权值weight,以source——>weight——>target形式返回。
完成ConcreteEdgesGraph。首先创建一个要放所有点的元组vertices和一个存放Edge的列表edges。
public boolean add(String vertex):如果vertices中不包含添加点,则将点添加到vertices中,返回true。否则返回false。
public int set(L source, L target, int weight):如果weight<0,直接返回,weight不能为负。否则进行边的增,删,改操作。删改都需要将原本的边删除,所以可以先搜索有无相同source和target的边,若有将weight记录下来,将边从列表中删除,此时删除操作已完成。而增和改此时都是加边,由于weight=0是删除操作,当weight>0直接将边加入列表中并用add操作将点加入即可。返回之前记录的weight值,若没有则之前没有此边,返回的即为0。
public boolean remove(L vertex):如果有此点则将它在vertices中删除,并将edges中所有包含此点的边删除,返回true。否则返回false。
public Set vertices():返回vertices的元组即可
public Map<L, Integer> sources(L target):根据参数target,找到edges中所有包含target的边,将边的source和weight加到map中,返回这个map。
public Map<L, Integer> targets(L source):根据参数source,找到edges中所有包含source的边,将边的target和weight加到map中,返回这个map。
public String toString():遍历所有边,将所有边的toString生成的字符串连在一起,输出。
结果:对ConcreteEdgesGraph进行测试,全部通过,覆盖率达92.4%
在这里插入图片描述在这里插入图片描述

3.1.3.2Implement ConcreteVerticesGraph

实现:由于是图的点实现,所以需要定义Vertex类,在类中有一个点以及从这个点出发连向的所有点和权值组成的map。类中应包含返回出发点,终点和权值map的函数。然后用由所有Vertex组成的列表完成图的实现的各类函数add,set,remove,vertices,sources,targets。
过程:先完成Vertex类的实现,点在类中定义为私有变量出发点source,target和weight组成map,根据这两个变量构成Vertex。
在Vertex类中实现返回出发点source,终点target和权值weight组成的map函数,直接返回对应的变量即可。完成toString函数,以字符串形式显示出来,直接将出发点source,map中的终点targets,权值weights,以source——>weight——>target形式返回。
完成 ConcreteVerticesGraph。首先创建一个要放所有点Vertex的列表vertices。
public boolean add(String vertex):如果vertices中的每个Vertex中的source都不是添加点,则构建一个Vertex,将vertex作为source,将Vertex添加到vertices中,返回true。否则返回false。
public int set(L source, L target, int weight):如果weight<0,直接返回,weight不能为负。否则进行边的增,删,改操作。删改都需要将原本的边删除,所以可以先搜索有无相同source的Vertex,Vertex中的map中有无target,若有将weight记录下来,在map中将key=target删除,此时删除操作已完成。而增和改此时都是加边,由于weight=0是删除操作,当weight>0用add操作将点加入vertices,搜索对应的source,在map中加入key=target,value=weight。返回之前记录的weight值,若没有则之前没有此边,返回的即为0。
public boolean remove(L vertex):搜索此点,如果此点为Vertex中source,则删掉此Vertex;若此点为map中的key,则将map中此key删除。返回true。否则返回false。
public Set vertices():将vertices中所有Vertex的source放入一个元组中,返回。
public Map<L, Integer> sources(L target):根据参数target,搜索vertices找到Vertex中对应的source和map中的value将source和value加到map中,返回这个map。
public Map<L, Integer> targets(L source):根据参数source,搜索vertices找到相同source的Vertex,返回targetmap即可。
public String toString():遍历所有Vertex,将所有的toString生成的字符串连在一起,输出。
结果:运行测试通过,覆盖率88.8%。
在这里插入图片描述
在这里插入图片描述

3.1.4Problem 3: Implement generic Graph

设计:进行修改扩充,使得点的类型可以为任意类型。由于基本实现已经完成,所以此处实现比较简单。

3.1.4.1Make the implementations generic

实现:按给出的例子,进行修改,将点类型String改为L。
在这里插入图片描述

过程:Edge 或 List 等类型的变量需要成为 Edge 和 List<Edge>。
类似地像 new ConcreteEdgesGraph() 或 new Edge() 这样的构造函数将需要变为 new ConcreteEdgesGraph() 和 new Edge()

结果:测试仍可通过,graph的覆盖率达到90.5%
在这里插入图片描述

3.1.4.2Implement Graph.empty()

实现:在Graph.empty中直接返回一个新的ConcreteEdgesGraph。
在这里插入图片描述

增加GraphStaticTest.java中对其他标签类型的测试,较为简单,不做过多描述
在这里插入图片描述

结果:测试仍可通过,graph的覆盖率达到90.5%
在这里插入图片描述

3.1.5Problem 4: Poetic walks

设计:复用之前完成的图操作,将一段文本的各个词作为点,各词之间的联系作为边的权。即:此b出现在a后几次,则a到b的边权为几。根据输入语段和完成的文本图,查找文本图中同时出现在输入语段两词a b,a后b前的词。将此词加入a b之间,以此类推,完成对输入语段的扩句

3.1.5.1Test GraphPoet

实现:测试应使用一个或多个示例语料库文件。将语料文件1.txt放在 test/P1/poet中。
过程:内容为From time to time,sadness fills my throat and pain.测试当输入语句为From time to sadness时,生成的语句是否为From time to time sadness。

3.1.5.2Implement GraphPoet

实现:定义一个之前实现的空图。将文本语料进行空格和符号的分词,分成一个个单词。从前到后读取两个相连的单词,通过pre_weight=graph.set(words[i].toLowerCase(), words[i+1].toLowerCase(), 0);会将图中已经存在的权值返回记录到pre_weight中,并将已经存在的删除。再通过graph.set(words[i].toLowerCase(), words[i+1].toLowerCase(), pre_weight+1);将新的权值记录下来。而若不存在,因pre_weight初始值赋为0,再加1刚好对应当前权值。
过程:
在这里插入图片描述

结果:生成了一个关于语料文件的可以反映词之间关系的图。

3.1.5.3Graph poetry slam

实现:将输入语句分为一个个单词,从前到后读取连续的两个单词,前面的作为source,后面的作为target,source通过graph.targets获取目标点map,target通过graph.sources获取出发点map。寻找两个map中的相同key的词,如果有将此key在两个map中的value的和相加,将结果最大的那个key加入source,target之间,扩充。如果没有,则不扩充,继续向后找。
过程:
在这里插入图片描述

结果:生成结果正确,测试通过
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.1.6Before you’re done

已进行检查,均已完成。
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
git commit -a -m “no1”
git remote add origin git@github.com/ComputerScienceHIT/HIT-Lab2-1190202002.git
git pull --rebase origin master
git push -u origin master

在这里给出你的项目的目录结构树状示意图。
在这里插入图片描述

3.2Re-implement the Social Network in Lab1

基于 3.1 节 Poetic Walks 中定义的 Graph及其两种 实现,重新实现 Lab1 中 3.3 节的 FriendshipGraph 类。
注 1:可以忽略你在 Lab1 中实现的代码,无需其基础上实现本次作业;
注 2:在本节 FriendshipGraph 中,图中的节点仍需为 Person 类型。故你的新 FriendshipGraph 类要利用 3.1 节已经实现的 ConcreteEdgesGraph 或 ConcreteVerticesGraph,L 替换为 Person。根据 Lab1 的要求, FriendshipGraph 中应提供 addVertex()、addEdge()和 getDistance()三个方法 : 针对 addVertex() 和 addEdge() ,你需要尽可能复用 ConcreteEdgesGraph或 ConcreteVerticesGraph中已经实现的 add() 和 set()方法,而不是从 0 开始写代码实现或者把你的 Lab1 相关代码直接复制过来;针对 getDistance()方法,请基于你所选定的 ConcreteEdgesGraph 或 ConcreteVerticesGraph的 rep 来实现,而不能修改其 rep。
注 3:不变动 Lab1 的 3.3 节给出的客户端代码(例如 main()中的代码),即
同样的客户端代码仍可运行。重新执行你在 Lab1 里所写的 JUnit 测试用例,测试你在本实验里新实现的 FriendshipGraph 类仍然表现正常。

3.2.1FriendshipGraph类

思路:FriendshipGraph类有三个功能:添加成员addVertex,增加有向边addEdge,计算两个人相连的最短距离getDistance(自己和自己距离为0,无法相连输出-1),以及返回生成图verticesGraph。
过程:addVertex:调用graph.add()直接添加。
addEdge:调用graph.set()进行设置边。
getDistance:采用先广搜索,构建一个队列,并构建一个HashMap<Person, Integer> map。将出发点的人添加到队列,添加到map的Key处,0加到Value处,读取队首,poll队首,读取他的每个朋友,若其朋友不在map中,则将其朋友全部添加到队列,并map.put(朋友,队首人的value+1),如果队列不空则一直循环,在循环中如果找到了终止点的人,则返回这个人在map中对应value值,即为距离。否则返回-1。
示意如下图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果:
添加成员addVertex。
增加有向边addEdge。
getDistance输出两个人相连的最短距离,自己和自己距离为0,无法相连输出-1。

3.2.2Person类

思路:建立Person类,由于各功能都能用图来实现,所以此时无需定义任何操作,只建立Person类即可。

3.2.3客户端main()

实验要求中已经给出,不做过多赘述。就进行了创建人员,添加人员,建立朋友关系,输出距离。
在这里插入图片描述

3.2.4测试用例

思路:测试用例可仿照3.3.3中给出的进行设计。创建人员,添加人员,建立朋友关系,输出距离。并进行断言。
过程:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2.5提交至Git仓库

如何通过Git提交当前版本到GitHub上你的Lab3仓库。
git commit -a -m “no2”
git remote add origin git@github.com/ComputerScienceHIT/HIT-Lab2-1190202002.git
git pull --rebase origin master
git push -u origin master

在这里给出你的项目的目录结构树状示意图。
在这里插入图片描述

4实验进度记录

日期 时间段 计划任务 实际完成情况
2021.6.6 20:00-23:00 将P1中graph完成 只完成了graph中的边实现
2021.6.10 12:00-22:00 将代码全部写完 完成
2021.6.11 14:00-20:00 将实验报告完成 完成

5实验过程中遇到的困难与解决途径

遇到的难点 解决途径
在点实现的过程中,对点类的构造错误,导致后续无法完成对应的图的实现 在点类中添加对应的相连的点与权值构成的map
在循环中如果将循环到的删除,会导致循环错误,无法继续进行

改用迭代器iterator进行遍历删除操作。

6实验过程中收获的经验、教训、感想

6.1实验过程中收获的经验和教训

要充分利用好已经完成的程序,做好复用能大大提升工作效率,并减少代码量。设计好实验测试,用Coverage进行测试,查看未覆盖代码的问题。

6.2针对以下方面的感受

(1)面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
面向ADT编程,能很好地进行复用,更安全、结构性更好。面向场景编程,更加直观。
(2)使用泛型和不使用泛型的编程,对你来说有何差异?
泛型更加灵活,适用的情况更多。
(3)在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
随时进行测试,保证正确性。感觉很好,但不太适应。
(4)P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
减少工作量和代码量,而且复用能保证正确性和安全性。
(5)为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
保证ADT的正确和安全,减少出现问题的可能,愿意。
(6)关于本实验的工作量、难度、deadline。
工作量很大,难度对于初学者很高,不过给出的时间也很长,还是能够完成。
(7)《软件构造》课程进展到目前,你对该课程有何体会和建议?
对于面向对象有了更深刻的认识,感觉这门课主要学习的是Java的思想。

这篇关于2021年春季学期计算学部《软件构造》课程Lab 2实验报告的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/561792

相关文章

Qt 设置软件版本信息的实现

《Qt设置软件版本信息的实现》本文介绍了Qt项目中设置版本信息的三种常用方法,包括.pro文件和version.rc配置、CMakeLists.txt与version.h.in结合,具有一定的参考... 目录在运行程序期间设置版本信息可以参考VS在 QT 中设置软件版本信息的几种方法方法一:通过 .pro

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

安装centos8设置基础软件仓库时出错的解决方案

《安装centos8设置基础软件仓库时出错的解决方案》:本文主要介绍安装centos8设置基础软件仓库时出错的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录安装Centos8设置基础软件仓库时出错版本 8版本 8.2.200android4版本 javas

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

如何用Java结合经纬度位置计算目标点的日出日落时间详解

《如何用Java结合经纬度位置计算目标点的日出日落时间详解》这篇文章主详细讲解了如何基于目标点的经纬度计算日出日落时间,提供了在线API和Java库两种计算方法,并通过实际案例展示了其应用,需要的朋友... 目录前言一、应用示例1、天安门升旗时间2、湖南省日出日落信息二、Java日出日落计算1、在线API2

Ubuntu 怎么启用 Universe 和 Multiverse 软件源?

《Ubuntu怎么启用Universe和Multiverse软件源?》在Ubuntu中,软件源是用于获取和安装软件的服务器,通过设置和管理软件源,您可以确保系统能够从可靠的来源获取最新的软件... Ubuntu 是一款广受认可且声誉良好的开源操作系统,允许用户通过其庞大的软件包来定制和增强计算体验。这些软件