哈工大2021软件构造lab1 实验总结_哈尔滨工业大学2021软件构造实验一-程序员宅基地

技术标签: java  编程语言  软件构造  


前言

本次实验通过求解三个问题,训练基本Java编程技能,能够利用JavaOO开发基本的功能模块,能够阅读理解已有代码框架并根据功能需求补全代码,能够为所开发的代码编写基本的测试程序并完成测试,初步保证所开发代码的正确性。另一方面,利用Git作为代码配置管理的工具,学会Git的基本使用方法。


一、Magic Squares

一个n阶MagicSquare定义为一个n*n方阵,包含两两不等的正整数,其中每一行、每一列和对角线的数字之和都相等。
该部分任务主要分为两个部分:
(1)判断给定的五个文本文件中给出的数据是否符合幻方的定义;
(2)使用给定的一个静态方法生成幻方,并向另一个文本文件输出,再利用(1)中编写的判定方法判断数据的合法性。

1. isLegalMagicSquare()

思路与过程:
(1) 首先从文件中按行读出数据,每一行数据存入字符串数组myLine,用整型变量line记录读入的行数;
(2)调用.split("\t")方法将每行字符串myLine[i]分割为粒度更小的字符串,并用整型变量len记录当前行包含的分割后字符串个数。判断len是否与line相等,若不相等说明给定数据不以方阵形式组织,不符合幻方定义。
(3)保证文件数据为方阵后,利用正则表达式[1-9][0-9]*判断当前行每个分割后字符串是否为正整数,若不符合则数据表示不符合幻方要求。若符合正则表达式,则利用Integer.valueOf方法将该分割字符串转化为正整数存入整形数组matrix。

正则表达式判断部分代码如下:

for (int j = 0; j < len; j++) {
    
		if (!strmatrix[i][j].matches("[1-9][0-9]*")) {
    
			System.out.print("error info: " + strmatrix[i][j] + " in "+filename+" is illegal\n");
			return false;
		}
			matrix[i][j] = Integer.valueOf(strmatrix[i][j]);
}

(4) 定义行和数组sumOfRow与列和数组sumOfCol, 用于记录每行与每列的和, 定义主对角线和sumOfDiagonal1与副对角线和sumOfDiagonal2。遍历matrix完成求和后, 将主对角线和设置为标准值, 比较主副对角线和。若两个和值不一致, 提示错误信息并返回false错误。比较行列和数组中每个数据。若过程中发现某个数据与标准值不符, 同样提示错误信息返回false。

2. generateMagicSquare()

主要思路:
正奇数幻方的产生方法,也就是generateMagicSquare()使用的方法可以用口诀来概括:“一居上行正中央,依次斜填切莫忘;上出框时向下放,右出框时向左放;排重便在下格填,右上排重一个样”。
具体来说,添加代码判断输入的n不为负数或偶数后,函数generateMagicSquare首先将数字1放置在幻方最上方正中央位置,而后每次将下一个数放置在右斜上方位置,每放置n个数字行数加一。若到达第一行,则下一次到最后一行放置,否则行数减一;若到达最后一列,则下一次到第一列放置,否则列数加一。放完所有n*n个整数后,将矩阵输出到文件6.txt。

二、Turtle Graphics

该实验的目的主要是通过Java实现一个简单的Turtle小程序并实现简单图形绘制的功能。

Problem 1: Clone and import

从实验手册链接领取P2初始任务代码,克隆到本地仓库,导入eclipse项目目录。

Problem 3: Turtle graphics and drawSquare

Turtle graphics实验任务组包含了“小海龟画图”GUI的简单Java实现版本。实验的主要任务是根据实验指导完成一些功能代码的阅读与补全。
函数drawSquare的实现要求利用forward和turn方法完成一个正方形的绘制。forward方法能够让小海龟在画面上前进给定单位距离,turn方法能够让小海龟又转向90度。
由于每次绘图开始时海龟朝向Y轴正方向,因此只需让小海龟前进sideLength个单位,再右转90度,重复4次即可完成一个正方形的绘制。

Problem 5: Drawing polygons

首先完成函数calculateRegularPolygonAngle(int sides)的实现。该函数接收参数为多边形的边数,返回值为对应多边形内角大小(以度为单位),数值类型为double。由正多边形内角计算公式,可以直接返回180.0 * (sides – 2) / sides。
接下来,运行TurtleSoupTest.java文件,测试对函数calculateRegularPolygonAngle的实现是否正确。
保证该函数实现正确后,要求完成对函数drawRegularPolygon的实现,且需要用到calculateRegularPolygonAngle的实现。实现方法也十分简单:只需要首先利用函数calculateRegularPolygonAngle计算出正多边形内角度数angle,再将turtle朝向右转(450-angle)度,而后进行sides(边数)次循环,每次循环先用forward方法前进sideLength距离,再使用turn方法转向(180-angle)度即可。

Problem 6: Calculating Bearings

该任务要求实现用于计算方位角的两个函数。
首先实现calculateBearingToPoint。该函数根据当前点,当前朝向与目标点坐标计算指向目标点的方位角。
直接利用Math.atan2函数计算出来由当前位置指向目的位置向量的与X轴正向的夹角,然后计算当前朝向的夹角,然后作差即可。主要不能出现负角度,可在适当时候增加360度达到此目的。

Problem 7: Convex Hulls

利用Gift wrapping algorithm算法计算给定集合中的凸包,首先找到最左下角的点加入集合,然后比较剩余点到此点的偏转角,找到偏转角最小的加入集合,当偏转角相同时,找到距离当前点最远的点加入集合。而后选择最新加入集合的点为基点,比较剩余其他各点到该点的偏转角,同样按照上述方法找到符合条件的点。以此类推,直到之后找到符合条件的点为起始点,得到凸包的点集。

Problem 8: Personal art

该部分要求利用以上实现各个函数实现个人图形的绘制。自由发挥即可。

submitting

在本地仓库文件夹下,打开git bash,键入命令行序列:
$git add .
$git commit -m “statement”
$git push origin master

三、Social Network

该任务要求实现Person和FriendshipGraph两个类,用FriendshipGraph来构建Person之间的关系来模拟一个社交网络,能够计算出每两个Person之间的社交距离。

1.设计/实现FriendshipGraph类

设计思路:考虑将FriendshipGraph类的实现与Person类的实现尽量独立,减少耦合。FriendshipGraph中顶点代表个人Person,有向边(A,B)代表B是A的单向好友,顶点与边采用邻接表的数据结构存储。FriendshipGraph类中除去两个实例构造方法,还包含其他四个方法:
(1)addVertex向社交网络中添加个人节点;
(2)addEdge向社交网络中添加单向好友关系;
(3)getDistance获取图中两人之间好友关系的最短路径长度;
(4)main方法负责测试实验指导中给出的样例数据。
其中,getDistance部分采用广度优先搜索从起点先广搜索目标节点并获取最短路径长度。
实现思路与过程:
1)邻接表部分,FriendshipGraph类中设置两个内部类:Edge类表示图中好友关系,包含Person类型成员person以及Edge类型成员next;Vertex类表示图中个人,包含Person类型成员person以及Edge类型属性next。

public class Vertex {
    
		Person person; 
	    Edge next; 
		Vertex(Person P, Edge next) {
    
			this.person = P;
			this.next = next;
		}
}
public class Edge {
    
		 Person person;
		 Edge next; 
		 Edge(Person P, Edge next) {
    
			this.person = P;
			this.next = next;
		}
}

同时,FriendshipGraph类中还包含Map集合vertexsMap,存储从Person到Vertex的映射,以保证后续不会有重复名字的Person被添加进社交网络;以及Map集合visMap,保证每次调用getDistance方法进行广度优先搜索时同一个顶点不会被搜索两次。

Map<Person, Vertex> vertexsMap; 
Map<Person, Boolean> visMap; 

2)由实验指导手册可知,FriendshipGraph应该支持默认参数构造和带Person类参数构造两种构造方法。

public FriendshipGraph() {
    
	this.vertexsMap = new HashMap<>();
	this.visMap = new HashMap<>();
}
public FriendshipGraph(Person P) {
    
	this.vertexsMap = new HashMap<>();
	this.vertexsMap.put(P, new Vertex(P, null));
	this.visMap = new HashMap<>();
	this.visMap.put(P, false);
}

3)Main方法与设计思路相似,添加实验指导中测试代码段即可。
4)addVertex方法方法接收参数Person P。若P为空或P不空,但P的身份无效(即Person中属性valid==false),则提示信息无效并返回false。若映射表vertexsMap中已经包含与P相同的键值,则给出添加非法提示并返回false,表示添加失败;否则,遍历vertexsMap的键值部分,若包含与P姓名相同的键值,则P不予添加,并调用P的denuStatus方法否决P的身份有效性,防止P进入后续操作,给出非法添加提示并返回false。若以上两次判重成功通过,说明P的身份独特性和有效性,新建P节点并将其添加至vertexsMap。
关键代码如下:

for(Person q: vertexsMap.keySet()) {
    
		if (q.getName().equals(P.getName())) {
    
				System.out.println("Error: The name \""+P.getName()+"\" has existed in records.");
				P.denyStatus();
				return false;
			}
}

5)addEdge方法接收有向边起始点begin和终点end(二者为Person类型),若二者中有一方为空,打印错误信息并返回false;若二者中有一方身份无效,则提示信息无效并返回false。若二者有一在图中没有对应顶点,同样打印错误信息并返回false。保证二者不空且在图中有对应顶点后,新建有向边并将其添加进begin的邻接表。关键代码如下:

Vertex beginVertex = vertexsMap.get(begin);
Vertex endVertex = vertexsMap.get(end);
if (beginVertex == null) {
    
	System.out.println("Error: The beginpoint "+begin.getName()+" does not exist");
	return false;
}
if (endVertex == null) {
    
	System.out.println("Error: The endpoint "+end.getName()+" does not exist");
	return false;
}

6)getDistance方法接收Person类参数begin和end,判定图中是否存在二者对应顶点,若不存在则打印提示信息并返回-1,否则将二者作为参数传入先广搜索方法BFS计算最短距离。
7)私有方法BFS接收起点个人start,目标个人end,以及顶点遍历标记visMap。若start与end相同,返回0;若二者其中之一为空返回-1;否则建立Map集合dis,记录起始点到各顶点距离Integer。而后通过广度优先搜索尝试寻找end,并更新距离信息。一旦在队列中发现end,返回end对应距离。
搜索过程需要用到辅助队列Queueq。起始时,q中只包含start顶点,且start访问标记为true,到自身距离为0;若队列q不空,则取出队头节点,遍历队头顶点的边表,依次将其中未被访问过的顶点添加至队尾,更新距离,而后判断顶点是否为end,若是则返回对应距离,否则继续。当循环结束后,若距离表dis中没有end的记录,说明start无法到达end,返回-1,否则返回end对应距离。

2. 设计/实现Person类

设计思路:Person类包含两个属性,分别是String类型的name表示姓名,和boolean类型的valid表示身份的有效性。除此之外,Person包含4个方法:带姓名的构造方法,获取姓名方法getName,获取身份状态方法getStatus,以及否定身份方法denyStatus。加入身份有效性的考虑在于,在不使用static类型全局变量的前提下,校验不同Person之间姓名的互异性成为一个不容忽视的问题。引入身份有效性后,通过将Person加入到社交网络时的行为来判定个人身份的有效性,即判重。

3. 设计/实现客户端代码main()

直接复制实验指导的代码稍加修改即可。

4. 设计/实现测试用例

设计思路:分别对addVertex, addEdge, getDistance,main四个方法进行测试。
1)对于addVertex,设计test用例testaddVertex,内建四个Person对象全部执行addVertex,判断vertexsMap中是否包含这些对象即可。
2)对于addEdge,设计测试用testaddEdge,内建8个Person对象,执行addVertex后,执行addEdge然后判断他们的朋友中是否包含彼此即可。
3)对于getDistance,设计test用例testgetDistance,内建8个Person对象,执行addVertex,addEdge后,测试三种距离等价类:距离为-1,距离为0,距离为正整数。
4)对于main方法,设计测试用例testSampleCase,测试其中代码执行结果是否与原版实验指导给出的相同。

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

遇到的困难 解决途径
Eclipse将java项目转换为maven项目后找不到主类 将目标源文件夹移出build path后再移入或
项目右击->maven->update maven project
Eclipse导入maven文件报错”Editor does not contain a main type” 将目标源文件夹移出build path后再移入或
项目右击->maven->update maven project
Git clone时报错OpenSSL SSL_read: Connection was reset, errno 10054: 设置git代理

五、总结

作为软件构造的首个实验,其任务量还是不小的,特别是对于此前缺乏JAVA编程经验的我来说,快速入门并且完成实验需要花费比较多的时间。但是总体来说,收获还是挺大的,起码对于Git的使用更为熟练一些。希望自己还是能扎实学些东西,好好提升一下自己的代码能力。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_35054078/article/details/117636210

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签