還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!

九章演算法發表於2017-04-18

專欄 | 九章演算法
網址 | www.jiuzhang.com

在九章做老師的這段時間,許多同學經常把一個程式直接貼給我說:

“ 老師,我的程式出錯了,幫我看看吧。 ”

對於這些同學的請求,我往往只能委婉的拒絕:

“ 除錯要靠自己哦~ 我幫你指出程式的錯誤,對於你自己一點幫助都沒有。你今天先繼續努力debug,明天要是還不能解決,我就幫你看看。 ”

雖然大部分同學最終也都能靠自己的努力找到錯誤,但是學會好的除錯技巧,確實能夠起到事半功倍的刷題效果。

於是我總結了四種我自己常用的在刷題過程中的除錯技巧,希望可以幫助到大家:

1. 技巧1: 列印中間結果

依賴於IDE的斷點除錯,是十分浪費時間的一種除錯方法。而且在面試中,你是基本沒有斷點除錯的機會的(因為很多公司是白板寫題,不會提供給你IDE)。事實上在實際的工作中,你也很少能夠有機會去進行斷點除錯,比如你進行的是 Web 開發,你只能想方設法的在程式碼中列印一些 Log,然後根據 Log 去分析出錯的原因。你平時用 IDE 寫程式碼,就十分容易養成這種斷點除錯的“壞習慣”。一個更好的方式,是使用列印中間結果的方式。以 LintCode 上的 Clone Graph 為例子:

還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!

LintCode 支援自己出測試資料進行測試的模式,點到“Testcase” 這個 tab之後,輸入測試資料,點 Run Code。你的程式中的輸出也會被顯示在 Output 中。我們看到 程式碼中 第22行到27行,是在列印一箇中間結果,這個中間結果就是在使用了 bfs 演算法之後,從一個點出發,找到的所有的圖中其他節點。這樣我們就可以迅速驗證,程式碼出錯到底是因為 BFS 寫錯了,還是因為其他的部分寫錯了。

使用這種除錯方法的另外一個好處是,你很自然的希望你的程式可以“分階段”輸出一些中間結果,這種分階段處理的方式,也是我們在課上經常強調的,把大問題變成小問題,然後逐個擊破的程式設計思想。養成這種程式設計習慣之後,可以非常有效的幫助你在寫程式的時候,就避免掉錯誤。

2. 技巧2: 一行一行改成參考程式

在九章的官網(九章官網link) 中有 LC 完整的參考程式庫,其中 Java 程式的一部分程式碼是我自己親自編寫的,具備較高的質量和參考價值。很多題目也給了多種不同的解法。比如 Binary Tree Level Order Traversal 這一題,給出了三種 BFS 的參考程式和 1個 DFS的參考程式:(三種 BFS 的參考程式和 1個 DFS的參考程式link)。通常來說很多題目,你不僅僅需要掌握其中一種方法,而是需要掌握這個題所有的解決辦法。

有不少同學經常會來問我:“老師,我的程式碼和參考程式一樣啊,為什麼還是不對。” 這種時候,我通常建議他們,將自己的程式碼,一行一行的改成參考程式。這樣,當他們發現哪一行程式碼不一樣的時候,就自然定位到了錯誤。

舉一個例子,就以 Binary Tree Level Order Traversal 為例,寬度優先搜尋演算法(BFS)是面試必須掌握的一種演算法。來看看下面這個有錯誤的程式碼(請花3分鐘閱讀一下,並找出錯誤所在):

還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!

從程式思想上來看,好像並沒有什麼錯。很難直接發現有什麼問題。與參考程式逐行對比之後會發現,錯誤發生在第14行。

參考程式中,寫法是:

還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!

和上面的程式寫法是:

還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!

這兩種寫法存在巨大區別,原因是在迴圈體內部,我們會不斷往 queue 中放東西,這導致了queue.size() 的值是不斷變化的。而沒有起到我們只想迴圈當前這一層所有點的功能。

通過對這個程式逐行對比,我們能夠馬上學會如下的知識點:

1. for迴圈中間的判斷語句,在迴圈體的每一次迴圈中都會被重複執行,而不是在一開始就確定好執行多少次。


2. 為了做到BFS的分層遍歷,需要先把 size 取出來,再 for迴圈這個size,這是BFS分層遍歷中最關鍵的一句話。



3. 技巧3: 給小熊講程式

這個技巧,是我曾經參加高中生演算法競賽的時候,一位演算法競賽國家隊的前輩教我的。當時我的演算法水平可能已經還不錯了,但是程式碼質量很差,很容易寫出有bug的程式碼。這位前輩告訴我,可以放一隻 “小熊” 在你的電腦旁邊,一旦程式出錯了。就對著這隻小熊講你的整個程式碼是怎麼解決問題的。因為是給小熊講,所以你可以把它當作什麼都不懂,於是需要更加仔細的去講述你程式碼中每個細節,所以你需要一行一行的講,甚至連為什麼你要用 ArrayList 而不用 int[] 都要講得清清楚楚。講著講著,你就有可能突然發現你的錯誤所在了。

這個技巧的意義在於,你在寫程式碼的時候,腦子裡可能想著一個事情,但是打出來的程式碼,可能是另外一回事兒。或者你腦子裡想著有3個條件需要判斷,但是打的時候,漏掉了一個。當你把程式碼重新講一遍的時候,事實上是在重新整理你的邏輯,查漏補缺。這樣很容易就能夠發現你的錯誤。

4. 技巧4: 必殺,重寫一遍

大部分的bug,其實都是 typo。比如:

  • 大於符號和小於符號打反了
  • <= 寫成了 <
  • 本來要寫一句話幹什麼來著但是後來寫著寫著忘記了
  • if 後面忘了加 else
  • for 迴圈後面忘了加括號,導致程式碼跑到了迴圈體之外
  • 變數名打錯:for(int j=0;i<n;j++)

諸如此類,不勝繁舉。重新寫一遍程式碼, 往往都能 fix 這些問題。

5. 尾聲

除錯是在你出現 BUG 的時候,才需要做的事情。大家平時應該儘量去練習的是不要寫出有BUG的程式碼。這很難我知道,但是你應該以此為訓練目標,在你在 LintCode 上點選 “Submit” 之前,儘量的做到已經檢查過自己的程式碼,不要過分依賴於 Online Judge幫你判斷你的程式碼是否有錯,更加不要依賴於本地 IDE 的斷點除錯功能。LintCode上有一個指標,在(LintCode介面link)這個介面(登陸後可見):

還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!

這個指標非常的重要,他是判斷你的程式碼是否 Bug Free 的重要參考資料。如果你最近一個月內的這個資料在 3 以內,那麼說明你的程式碼具備非常高的質量,不太容易出bug。如果在3-4之間,說明,還可以有所提高。如果在4以上,說明,你的程式碼質量比較糟糕了。



歡迎關注我的微信公眾號:九章演算法(ninechapter)。
精英程式設計師交流社群,定期釋出面試題、面試技巧、求職資訊等

還在斷點除錯?教你四種除錯技巧讓你快速定位錯誤!
九章演算法,IT教育領域的深耕者

相關文章