【软件构造】Lab1基本流程指导及重难点分析
本篇文章旨在引导HIT软件构造Lab1的基本流程并进行一些重难点的解释,总体内容偏向基础,出发点是在实验过程中我遇到了很多问题并花了大量时间才解决,希望可以帮助大家更好地完成实验。
实验目标:本次实验主要目的是熟悉eclipse环境下的java编程,没有更多软件质量属性的要求,实现的程序满足功能即可。和学习练习使用git版本控制工具。
实验环境:需要安装java的一个编译器eclipse和版本控制工具git,直接去官网下载即可,关于git的使用和实验的提交方法部分在文章末尾。
问题1 Magic Squares
在处理问题之前请确保自己对java编程有了一定的基础,如果之前没有接触过java面向对象编程,请先学习java和面向对象思想,否则实验无法开展,相关课程在mooc上有很多。
这个问题题意是给出一个n*n的方阵,每个位置上包含一个整数,要求写一个程序判断这个方阵是不是一个Magic Squares,Magic Squares的定义是这个方阵每行n个数、每列n个数、每条对角线(总共两条对角线)n个数的和相等。不难看出,程序本身的实现没有难度,难点在于数据的读入和各种非法情况的判断。规定的文件中数据满足有n行,每行n个正整数,每行之间的正整数用tab符(\t)分割,如果格式错误需要停止程序运行并输出提示信息,下面给出一个读入数据和判断非法情况的示例。
1 try { 2 File file = new File(path); 3 FileReader reader = new FileReader(file); 4 BufferedReader bReader = new BufferedReader(reader); 5 String msline; 6 while ((msline = bReader.readLine()) != null) { 7 ms[line] = msline.split("\t"); 8 line++; 9 } 10 bReader.close(); 11 } catch (IOException e) { 12 System.out.println("错误:找不到文件"); 13 return false; 14 }
其中try-catch结构为异常捕获,防止文件路径有误。path为读入文件的路径,为相对路径,例如"src/P1/txt/1.txt"即可。第6行每次在文件中读入一行存入字符串msline中,第7行再将msline中的字符按tab符分割并存入字符串数组中,注意ms是一个二维字符数组。这样,全部信息都存入了ms数组。
之后需要判断的非法情况有文件每行元素间并非用tab符分割、存在元素不是正整数、方阵行列数不相等等。其中难点是tab符的判断,提供一个思路,提取每一个元素判断是不是一个数字,如果某个位置元素不是一个数字,则说明并非全部使用tab符分割。判断是否是数字可以使用正则表达式匹配。
ms[i][j].matches("-?[0-9]+.?[0-9]*")
非法情况清除完成后按要求判断每行每列每对角线是否相等,如果都相等说明是一个Magic Square。
接下来题目给出了一个程序,要求分析这个函数并测试,还需要给出中文注释。这个函数的功能是构造一个规格是奇数的Magic Square,使用的方法是一个斜着填数的方法,每次填分布合理的一组数,通过行列的合理变动使这一组数在行、列、对角线三个维度上都均匀填入,便可以保证填出Magic Square,可以用一个小数据手动执行一遍流程,帮助理解算法思想。之后要求扩展函数,产生非法情况时优雅退出,参考之前实现过的判断,加入一些非法判断,输出错误信息,停止程序执行。
问题2 Turtle Graphics
该问题要求使用一个名为turtle的画图工具实现一系列子问题,实现每个子问题的形式是“补全代码”,其中一些问题涉及到几何知识和算法。
其中需要用到turtle的方法有:
1 turtle.forward(len);//沿当前方向向前画长度为len的线 2 turtle.turn(angle);//将当前方向转动angle度 3 turtle.color(PenColor.BLUE);//将画笔颜色变为蓝色(BLUE可变为别的颜色)
其中我觉得最大的难点在于翻译题意,还有一个凸包算法,下面依次解释每个问题。
2.1 Clone and import
老师已经在github上给出代码,进入指定位置下载压缩包即可得到代码。
2.3 Turtle graphics and drawSquare
要求画一个正方形,使用forward方法和画线,turn方法转动直角,共四次即可。
需要补充的代码在TurtleSoup.java文件中,其中会有每个问题的spec,找到对应问题的spec后会注意到一条抛出异常语句:
1 throw new RuntimeException("implement me!");
在此语句位置写你的功能实现,写完后将这条语句删除或注释掉。找到最下面的main函数调用写好的方法即可测试实现情况(画图形式)。
2.5 Drawing polygons
要求画一个正多边形,这个问题需要实现两个函数,第一个函数实现求解正多边形的内角度数,正多边形的内角度数存在求解公式,可以自己推一下,使用推出的公式实现求内角。另一个函数中调用第一个函数结合forward方法和turn方法即可实现画一个正多边形。
这里还要求使用JUnit测试求正多边形内角函数,这个工具在之后的实验中会经常用到,用来测试程序的正确性。eclipse自带了JUnit不需要额外下载,只需要在编译器中操作,将JUnit加入library中即可使用。打开TurtleSoupTest.java文件,找到对应函数的测试代码,运行完成测试,若测试出现图标为绿色的对勾则测试通过,若存在不正确的测试点则说明程序有误,可以通过观察期望结果和实际结果来检查程序实现。
2.6 Calculating Bearings
第一个子问题是给定一个初始方向(通过角度给出)、一个起始点坐标、一个目标点坐标,求以起始点为端点、以初始方向为方向的射线转动多少度可以指向目标点。做法也比较简单,先求出从起始点指向目标点的向量,利用atan2函数可以求出角度,在将此角度与初始方向角度运算可得结果,注意角度的参照方向要一致,比如都设定为x轴正方向为0度。实现完成后进行JUnit测试。
第二个子问题是给定多个目标点,要依次求出转动角度,将刚才实现的函数多次调用得到结果。实现完成后进行JUnit测试。
2.7 Convex Hulls
给定n个点的坐标,要求求出这n个点形成的凸包上覆盖的点集。
凸包的定义通俗地来说就是在一个二维平面上的点集中将最外层的点依次连接起来形成的凸多边形,它能包含点集中所有的点。形象的说法是好比一个平板上插了很多钉子,拿一个橡皮套把它们全部套住,橡皮套形成的形状就是凸包,受力的钉子就是凸包覆盖的点。下面是凸包的一个示例,其中点GDKHJB是凸包覆盖的点。
凸包问题是一个很经典的问题,这里提供一种较为简便的做法。首先遍历一遍所有点,依据横坐标找到最靠左的点,若多个点同时最靠左则找这些点中依据纵坐标最靠下的点作为起始点,这个点一定在凸包上,将它加入点集。从这个点开始,初始方向为y轴正方向,寻找顺时针转角最小的点,若多个点在同一个角度上则选择距离最近的点,将这个点加入点集,用目前方向做更新,再次执行以上步骤。直到找回起始点,此时点集中的所有点就是凸包上的点,将它们加入set数据结构。完成后进行JUnit测试。
2.8 Personal art
自由发挥,随便画出一个迷惑图形。上面多给了一个变化颜色的方法,如果想用更多方法画出更迷惑的图形可以去查turtle的其他方法。
问题3 Social Network
这个问题需要实现一个边的长度固定的有向图,支持插入点、插入边、计算两点间的距离功能,需要具有一定的健壮性。并且涉及到了面向对象的程序设计思想。
我认为一个很大的难点还是在于题意翻译,不过这道题的英文比较日常,可以直接扔进百度翻译,基本能直接看明白。
需要实现三个类,FriendshipGraph类中实现整张有向图的存储和要求的各种功能。有向图可以使用邻接表实现,由于“点”的本质是一个人,名字是一个字符串类型,所以可以考虑使用map数据结构存储。查询距离时由于所有边的值相等,可以直接使用bfs来求,比较容易。Person类直线一个“点”,即一个人的相关信息,最简单的情况是只存储一个名字,也可以将FriendshipGraph类中的一些功能调到这里实现,比如存储一下与这个人相连的所有人。
事实上,这两个类的设计虽然很简单,但我认为就是面向对象程序设计思想的一个重点,即合理安排各个类的功能和类与类之间的关系和交互。尽管在这个小问题中,将有向图的存储这一内容放到FriendshipGraph类中还是Person类中实现并没有体现出多大区别,但在面对较大规模的开发时,这件事很大程度上决定了开发的效率和软件的质量,需要认真思考。
第三个类是客户端类,其中需要实现和用户的交互,可以选择在控制台输出指定格式之后读取输入执行的方式,写一个main函数使得整个程序可以运行。
另外题中提到了健壮性的问题,需要加入一些非法判断,例如两个人名字一样、一个人连向自己等等,不合法的输入需要判断出来,不执行本次操作并反馈错误信息。实现完成后需要进行JUnit测试,与之前不同的是这次需要自己实现测试程序,在test目录下新建测试类,一个测试类对应测试一个类,每个测试类中给出多组测试数据,每组对应被测试类中的一个方法。参考之前给出的测试程序写出本题的测试程序。
关于git的使用和实验的提交问题:
首先安装git,在本地的项目文件夹中右键选择git bash进入命令行界面,输入命令
git init
初始化git文件,之后输入命令
git add xxx.xxx
其中xxx.xxx表示需要提交的文件,前后都是空格则表示本目录下所有文件,之后输入命令
git commit -m "xxxx"
其中xxxx表示本次提交的注释信息,可以自由写。这样,文件在本地已经提交成功,之后需要想远程仓库(即github上的仓库)提交。
进入老师给出的链接,绑定自己的远程实验仓库,仓库名字改规范,之后在本地git bash中继续输入命令
git remote add origin xxxxxx.git
连接至远程仓库,其中xxxxx.git为远程仓库URL,在远程仓库中可以找到,之后输入命令
git push -u origin master
即可将本地仓库中的文件提交至远程仓库,此时刷新一下github,便可以在远程仓库中看到提交的文件。