软件构造——实验4之debug

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/lll_90/article/details/92234523

体会:下面debug的过程其实主要是通过eclipse中的静态检查和断点的单步调试实现的。只要根据期望输出与实际输出定位好bug的大致位置,然后仔细查看代码,判断问题可能出现的位置,缩小范围后,再进行单步调试,根据程序不合逻辑之处,找出bug所在。
其实,这种调试方式是最最基础和通用的,不同于后面的多线程并发,需要通过堆栈了解程序在运行时的状况。不过那部分操作还不太熟悉,需要多加巩固。好了,下面是基础debug部分:

3.6 Debugging

3.6.1 理解待调试程序的代码思想

FindMedianSortedArrays.Java
采用二分法,计算合并后数组c的中位数的位置:halfLen。当c长度为偶数时,halfLen即为中间两个数中的较大数的下标,为奇数时,halfLen恰为c的中位数的下标。
先取较短数组的中位数A[i],这里让A作为较小数组,假定A[i]为合并后数组的中位数,则B中必有halfLen-i-1个数小于A[i],特别地,当B为奇数长时,需要确定B中第halfLen-i大的数B[halfLen-i-1]是否比A[i]大。
通过二分法对A进行裁剪,同时移动i、j的位置,确定合并数组c由A、B中哪段构成。
继续二分A,知直到找到合适大小的划分点A[i]、B[j],使A[i - 1] < B[j]且A[i]>B[j - 1].
注意此处A为偶数长时,A[i]是中间两个数中的较大者,B[j] 为偶数长时,B[j]也是临界两个数中的较大者,故而m+n为奇数时,左侧的最大数即为中位数。

RemoveComments.Java
以“/”与“/”为标识确定comment的范围,用inBlock作为标记,,当inBlockfalse时,读入当前字符,当inBlocktrue时,将当前字符忽略。
对每个source[i]中的comments部分进行排除和筛选之后,添加到新的list中。

TopVotedCandidate.java
每个人的第i票存在A.get(i)的List中. 在查询时,凭借时间有序的特点,可知每个i的A.get(i).get(0).time按照i大小依次递增,而每个i中的A.get(i)的A.get(i).get(j).time中的时间又是按照j的大小有序排列。
因此,通过二分法先找到A.get(i).get(0).time中小于t的最大i,再在A.get(i)中寻找小于等于t的最大j。因为当两者选票最接近时,需要的是最近得票最频繁的人为winner,所以只要找到大于t的最小i、j即可。

3.6.2 发现并定位错误的过程

FindMedianSortedArrays.java
首先参考了网上这种算法的核心思想:二分查找思想,将a数组的中间元素与b数组的“中间元素”相比较,从而删掉较小元素所在数组的前一半和较大元素所在数组的后一半。
Debug:
将spec中的examp写入测试用例,发现所求得的结果总是与原来差1、2个数,猜测在取中间值时未处理好。下面分析代码:

  1. 起始处,未考虑到整除的特性,应当将下图中红框中的语句改为halfLen = (m+n+ 1) / 2;
    在这里插入图片描述
    2.根据整除的性质与数组下标从0开始,A的中位数的下标应当为int i = (iMin + iMax) / 2;
    3.根据合并后数组c的奇偶性判断左边最大数是否为中位数。应当使用c的长度求余2.
    这里将在这里插入图片描述 ,改为在这里插入图片描述 RemoveComments.Java

  2. 通过IDEA的 静态检查,发现语法错误在这里插入图片描述 ,并将其修改为 在这里插入图片描述

  3. 运行时报错,发生数组越界,发现错误:
    在这里插入图片描述
    将其改为在这里插入图片描述

  4. 发现当添加单个source[i]时inBlock的条件设置不正确,应改为在这里插入图片描述

  5. 继续测试,发现输出字符中会有‘/’,分析代码,发现当读到“/”的‘’符号时,已将inBlock修改为false,会在读‘/’时将其默认为需要读入的字符,因此,增加i++;跳过该字符在这里插入图片描述

  6. 继续测试,发现,程序会读入以“//”表示的comment,于是增加筛选“//”批注的功能在这里插入图片描述

直接跳出循环,将当前行已读入的部分添加到list。

TopVotedCandidate.java

根据IDEA的静态检查,发现语法错误如下图:
在这里插入图片描述

先根据spec写好测试用例,本次实验中,本人用的是完全与原来相同的测试用例。
由q数组进行6次测试:
* 1.A有票,B没票
* 2.A、B均有票,且A<B
* 3.A、B均有票,且A=B,B获得票最近(2个例子)
* 4.A、B均有票,且A=B,A获得票最近
* 5.A、B均有票,且A>B
发现陷入死循环。怀疑在二分法使用过程中边界处理不正确,于是根据思路进行了检查,发现如下错误。
1.易知,count是用来为每个candidate计票的,发现,c并没有增加,作如下修改:
在这里插入图片描述
2.而后,发现在对A.get(i)使用二分法求i时,当A.get(mi).get(0).time太小且lo=hi-1时,会由于整除的特性,使lo得不到增加,这里陷入了死循环。如下图
在这里插入图片描述
将lo = mi;改为lo = mi + 1;
3.由判断条件if (A.get(mi).get(0).time <= t)可知,当A.get(mi).get(0).time等于t时,仍要将查询区间右移,可知最后的A.get(lo).get(0).time应当是大于t的;且A.get(lo).get(0).time是A.get(lo)中时间最小的点,所求的分解选票一定是在比t大的最小整数i的A.get(i)序列里的。
应当在A.get(lo-1)序列中寻找。
将上图中的int i = lo;改为int i = lo-1;
4.因为查询的时间点所投的选票也统计在内,因此lo是使A.get(i).get(lo)比t大的最小整数,而不是大于等于t的,这里将下图中红框内的条件改为A.get(i).get(mi).time <= t
在这里插入图片描述

5.最后是j的取法,显然,lo>=0,最后一句没有意义,但是考虑到A.get(i).get(0).time可能是小于等于t的最大时间,此时有lo-1<0.明白这是极端状况的处理,将原来语句改为
int j = Math.max(lo-1, 0);

3.6.3 如何修正错误

详情见3.6.2
修改如下:
FindMedianSortedArrays.java
1.将halfLen = (m+n) / 2;改为halfLen = (m+n+ 1) / 2;
2.将求A数组中位数下标的语句int i = (iMin + iMax + 1) / 2;改为int i = (iMin + iMax) / 2;
3.将求合并后数组奇偶性的条件判断if ((m + n +1) % 2 == 1)改为if ((m + n ) % 2 == 1)

RemoveComments.Java的修改详情间3.6.2 RemoveComments.Java部分

TopVotedCandidate.java

  1. 在这里插入图片描述改为在这里插入图片描述

2.将在这里插入图片描述 改为在这里插入图片描述

  1. 中位数过小而进行的移动中,将lo = mi;改为lo = mi + 1;
    4.将选取A.get(i)时,int i = lo;改为int i = lo-1;
    5.将裁剪范围时,条件判断A.get(i).get(mi).time < t改为A.get(i).get(mi).time <= t
    6.将求j时的边界处理int j = Math.max(lo, 0); 改为int j = Math.max(lo-1, 0);

猜你喜欢

转载自blog.csdn.net/lll_90/article/details/92234523