哈尔滨工业大学软件构造Lab1总结(2023春)

目录

1.实验目标

2.实验环境

3.实验过程

3.1Magic Squares

3.1.1 isLegalMagicSquare()

3.1.2generateMagicSquare()

3.2Turtle Graphics

3.2.1Problem 1: Clone and import

3.2.2Problem 3: Turtle graphics and drawSquare

3.2.3Problem 5: Drawing polygons

3.2.4Problem 6: Calculating Bearing

3.2.5Problem 7: Convex Hulls

3.3Social Network

3.3.1设计/实现FriendshipGraph类

3.3.2设计/实现Person类 

 3.3.3设计/实现测试用例

4.总结


1.实验目标

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

-基本的 Java OO 编程

-基于 Eclipse IDE 进行 Java 编程

-基于 JUnit 的测试

-基于 Git 的代码配置管理

2.实验环境

        java8,IDEA

3.实验过程

3.1Magic Squares

        要编的第一个函数是判断的5个文件中的数字序列是否为幻方,返回true或false。在遇到错误时要判断错误类型,给出错误提示,并返回false。可能出现的错误有数字之间没有用“\t”分隔,数字序列不是矩阵,数字序列是矩阵但行列数不同,数字不为正整数,数字的行之和与列之和和对角线之和不相等。

3.1.1 isLegalMagicSquare()

        首先步骤为文件读写,此处采用BufferedReader字符流读入文件,一次读入一行,读文件和关闭文件流时会产生编译时错误,在函数后抛出异常即可处理。如下所示:

public static boolean isLegalMagicSquare(String fileName) throws IOException

        第二个关键点为判断输入是否合法,读文件时已经将txt文本中的每行以字符串读入了字符数组中,并已经用string.split(“\t”)分割完毕,如下:

while((s = bfr.readLine()) != null){
    str_matrix[count] = s.split("\t");
    num[count] = str_matrix[count].length;
    count++;
}

        故可直接用正则表达式”\\d+”判断读入的字符串是否合法,即判断是否有非正整数的输入或者未用\t分隔。注意到幻方矩阵行列数一定相同,故可用一循环判断输入是否为矩阵或行列数是否相同。

        第三步相对简单,矩阵输入已经合法,用sum变量直接保存任意列(或者任意行)的数字和,分别与每行每列和两对角线的数字和对比,如果对比中任意一次不同,即可返回false。最后,如果通过了所有的比较,在函数末尾直接返回true即可。

3.1.2generateMagicSquare()

        n为奇数生成流程:首先在第一行的中间赋值为1,然后遵循如下规则:优先判断当前数字是否为n的倍数,如果是,就在该数字的下一行(正下方)赋值该数字+1;如果不是,就给当前数字右上角的数字赋值当前数字+1,如果该数字在上边界,就跳跃到正下方的下边界右面一个位置赋值,如果在右边界,就就跳跃到正左边的左边界上面一个位置赋值。遵循此规则,赋值n*n次后结束,具体流程图如下:

        当n输入为偶数时,矩阵赋值不能按照此规则赋值,因为在赋值的时候会在对角线越界赋值,从而会报错ArrayIndexOutOfBoundsException(越界访问错误);当n输入为负数时必然是不合法的,因为不存在长度为负数的数组,所以会报错NegativeArraySizeException(负数大小错误)。

        故可以采用try/catch语句对错误进行捕获,进行“优雅的退出”各个catch语句如下所示:

catch (ArrayIndexOutOfBoundsException e)
catch (NegativeArraySizeException e)
catch (FileNotFoundException e)
catch (IOException e)

        把矩阵写入文件采用了OutputStreamWriter流,需要注意的是在关闭时需要先关闭OutputStreamWriter流,再关闭FileOutputStream流(即先打开的后关闭),否则会报错提示关闭失败。写入成功后调用isLegalMagicSquare测试,会显示矩阵符合要求。

3.2Turtle Graphics

        该任务需要我们clone已有的程序后,利用turtle按照要求画图,其中需要利用几何知识设计一些函数简化编程,最后可以发挥想象力进行Personal Art。

        初次导入项目时,每个文件中的代码均会报出不同数量的错误,经分析后这些错误主要来源于导入包名错误和JUnit未在idea中导入,解决办法如下:

        1.将每处包名导入错误补写完整,即加上P2.,示例如下:

package P2.turtle;

        因为所有文件和包都在P2下,故需要加上P2.。除了开头引用部分,各个方法处或方法内部也有同样情况出现,补写完整即可。

        2.在idea中导入JUnit,具体步骤如下:

        打开File下的这一栏:

        在其中的Libraries点击左边的加号(截图时间为导入完成后)

 

        点击Java一栏,在idea的安装目录下找到lib文件夹,在其中找到junit4包

        之后点击OK保存后即可,导入成功后Test文件中不再提示” Cannot resolve symbol 'junit' ”。 

3.2.1Problem 1: Clone and import

使用git提交即可

3.2.2Problem 3: Turtle graphics and drawSquare

        该部分要实现drawSquare函数画一个正方形,利用forward函数和turn函数可以十分容易的实现,只需要循环四次前进画线和转弯90°即可,200单位的正方形如下:

3.2.3Problem 5: Drawing polygons

        该方法要求实现计算多边形内角的函数,注意合法性检验,当输入sides小于2时应打印错误语句,当sides合法时,由几何关系可得角度公式如下:

double angle = (double)180.0 - (double) 360.0 / (double) sides;

        在这之后对TurtleSoupTest进行junit测试,这里使用了idea中的junit4 Parallel Runner插件进行测试,可以看到testAssertionsEnabled和calculateRegularPolygonAngleTest通过了测试,符合预期。

        利用已经获得的calculateRegularPolygonAngle方法,我们可以计算出正多边形的内角,所以在drawRegularPolygon方法中只要给定正多边形的边数和边长就可以画出,只需要循环sides次,每次转过的角度为180.0 – 内角,循环代码如下:

for (int i = 0; i < sides; i++) {
    turtle.forward(sideLength);
    turtle.turn(180.0 - angle);
}

3.2.4Problem 6: Calculating Bearing

        首先我们要计算turn从当前点到目标点所需的参数,以当前方向作为附加参数。例如,如果海龟在 (0,1) 处面向 30 度,并且必须到达 (0,0),它必须再转 150 度,所以calculateBearingToPoint(30, 0, 1, 0, 0)会返回150.0。利用Math.atan2函数直接返回角度值,由数学关系得:

double angle = Math.atan2(targetY - currentY,targetX - currentX) * 180.0 /Math.PI;
angle = angle < 0 ? -angle : 360.0 - angle;
return (90.0 - currentBearing + angle) % 360.0;

        因为考虑到结果需要在0-360.0区间内,所以结果应该%360.0。

        calculateBearings函数较为简单,直接利用上面的calculateBearingToPoint函数即可,注意每次记录转过角度,并与已经转过角度相加:

for (int i = 0; i < xCoords.size()-1; i++) {
    temp = calculateBearingToPoint(angle,xCoords.get(i),yCoords.get(i),xCoords.get(i+1),yCoords.get(i+1));
    list_angle.add(temp);
    angle += temp;
    temp = 0;
}

​​​​​​​3.2.5Problem 7: Convex Hulls

        该函数要求从给定点中求出在点集凸包边界上的点,这里采用的是礼品包装算法。顾名思义,礼品包装算法就是先找到一个一定在边界上的点,然后逐渐的找到其他在边界上的点,将所有点像包装礼品一样包起来。

        先找到在最左侧(左下角)的点(即x最小,x一样情况下找y最小),该点一定在边界上,然后顺时针寻找与当前朝向夹角最小的点加入集合,并更新朝向(当前朝向偏转角加旋转角),一直如此找下去,直到找到第一个点。此处可以利用之前写过的calculateBearingToPoint函数,该函数只是传入参与我们的目标不符,但功能上两者一致,无需修改内部代码,所以对该函数进行拓展如下:

public static double calculateBearingToPoint(double currentBearing, double currentX, double currentY,
                                             double targetX, double targetY) {
    double angle = Math.atan2(targetY - currentY, targetX - currentX) * 180.0 / Math.PI;
    angle = angle < 0 ? -angle : 360.0 - angle;
    return (90.0 - currentBearing + angle) % 360.0;
}

 如此一来求凸包的工作会简单许多。先找到最左下角的点:

while (ie.hasNext()) {
    Point a = ie.next();
    if ((Double.compare(temp.y(), a.y()) == 1) || ((Double.compare(temp.x(), a.x()) == 0) && (Double.compare(temp.y(), a.y()) == 1))) {
        temp = a;
    }
}

然后遍历寻找即可。

3.3Social Network

        该任务要求构建一个关系网络图,需要人作为图的顶点,顶点之间需要存在有向边,因为这个任务要构建的是一个无向图,所以当确定两个人之间是朋友关系时,需要添加双向有向边。

        这个社交网络图需要可以加入顶点、添加朋友关系、计算两个人在社交网络上的最短距离,同时还要能检测是否有重名的人,Person这个类里面至少要有名字这个属性。

​​​​​​​3.3.1设计/实现FriendshipGraph

        该类中要实现三个函数:添加点,添加单向边和计算两个点最短距离。故采用HashMap来存储整个关系图:

private final HashMap<Person, HashSet<Person>> friends;

        确定了合适的实现方式,下面实现起来就比较容易了。添加点的函数需要检测是否输入了同一个人(每个人名字唯一),如果重复就进行报错:

while(ie.hasNext()){
    Person a = ie.next();
    if(per.getName().equals(a.getName())){
        throw new IllegalArgumentException("输入名字重复!");
    }
}

添加边的函数需要检测需要添加边的点是否在图内,以及需要添加的边是否已经存在:

寻找最短距离使用广度优先搜索,核心代码如下:

 

3.3.2设计/实现Person类 

Person类测试比较简单,只需要拥有赋值和获取名字的功能:

 3.3.3设计/实现测试用例

        addVertexTest函数测试正常输入情况下函数是否返回true,以及重名情况下是否能catch到异常;addEdgeTest测试正常输入情况下是否返回true,以及点不存在是否能catch到异常,还有在边已经存在时是否返回false;getDistanceTest函数测试两个点之间有路径时的距离,无路径时是否返回-1,自己与自己是否返回0,以及查找的点不存在时是否能catch到异常。

4.总结

        作为第一个软构实验,对于代码新手难度可以算得上较大(大佬请忽略),全新软件的使用、代码思路的构建和实验要求的分析都是难点,但同时也是一个锻炼的过程(自学能力就是在这种过程中锻炼出来的,笑),所以还是要多查资料,多思考。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值