軟件構造——實驗4之debug

體會:下面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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章