本文是Google工程師Steve Merritt的一篇博客,向大家介紹他自己和身邊的同事解決編程問題的方法。
原文地址:blog.usejournal.com/how-a-googl…
在本文中,我將完整的向你介紹一種解決編程問題的策略,這個策略是我在日常工作中一直使用的,并且用它來幫助各個等級的程序員(包括新手、大學生和實習生)學習和成長。應用這個結構化流程可以最大幅度的減少那令人沮喪的調試時間,并且能夠在盡可能短的時間內編寫出更加整潔、錯誤率更低的代碼。
一步步
接下來我將用一個栗子來說明。
問題:有兩個字符串sourceString
和searchString
,返回searchString
在sourceString
中第一次出現的位置,如果searchString
不是sourceString
的字串,就返回-1。
第一步:畫下來
當你拿到一個需求,馬上就開始著手寫代碼是一個非常愚蠢的主意。在寫一篇文章之前,首先要弄清楚論證和論據,并且你要保證論證是有意義的。如果你沒有這么做的話,當你意識到你所寫的東西前言不搭后語時,你可能會因為浪費了大把的時間而想請自己吃一頓大嘴巴子。編程也是一樣的道理,甚至比這還嚴重,嚴重到像洗澡的時候把洗發水弄進眼睛里。
問題的解決方法通常很重要,即使它看上去很簡單。在寫代碼之前,首先要做的就是把這個方法在紙面上呈現出來,并且保證在不同的情況下適用。
所以不找急著寫代碼,甚至都不要思考如何寫。后面你會有充足的時間去敲代碼,在這之前,你要把自己當成一臺計算機,弄清楚你這臺計算機會怎么解決這個問題。
你可以使用流程圖,或者使用其他能幫你具象化的方法,總之我們的目標是解決問題。你可以用紙和筆隨意發揮,不需要收到鍵盤的限制。
我們從一些簡單的情況開始,如果一個方法是“輸入一個字符串”,那么“abc”可以作為第一個例子。首先要搞清楚正確的結果是什么,然后去想怎么樣得到正確的結果,并且一步一步的進行。
我們假設輸入的字符串是這樣:
sourceString: "abcdyesefgh"
searchString: "yes"
復制代碼
我的想法是這樣的:
我看到了searchString
在sourceString
里,但是我要怎么做呢?我從sourceString
的第一個字符開始,逐字去讀,一直到最后,判斷每一個三個字符是不是yes
。比如abc
,bcd
,cdy
等等。當index值為4時,我找到了字符串yes
,所以我知道結果是index為4。
當我們寫下算法時,必須要保證我們考慮了所有的情況,并且處理了所有可能的場景。當我們找到匹配的字符串時,返回結果。如果找不到匹配的字符串,同樣要返回結果。
我們來嘗試另一組字符串:
sourceString: "abcdyefg"
searchString: "yes"
復制代碼
我們重復剛才的操作,當我們讀到下標為4的字符時,找到的字符串是yef
,這是最接近的結果了,但是第三個字符卻不同,所以我們繼續往后讀,一直到最后,沒有找到匹配的字符串,所以我們決定返回-1。
我們確定這一系列步驟(程序設計中,我們稱之為算法)解決了我們的問題,并且處理了兩種不同的場景,每次都得到了正確的結果。這時,我們就對我們的算法比較有信心了,并且可以將它形成條目。我們一起來進行下一步:
第二步:用英語寫下來
這里我們考慮將第一步形成的算法用英語寫下來。這可以使每一步變得更加具體,以便我們后面寫代碼的時候有所參考。
- 從字符串的第一個字符開始
- 查看每一組3個的字符(其實是
searchString
的長度) - 如果匹配上
searchString
,就返回當前的index - 如果一直到末尾都沒有找到匹配的字符串,就返回-1
看起來很棒,不是嗎
第三步:寫偽代碼
偽代碼并不是真正的代碼,只是一種模擬形式,這里我寫下上面的算法的偽代碼:
for each index in sourceString,there are N characters in searchStringlet N chars from index onward be called POSSIBLE_MATCHif POSSIBLE_MATCH is equal to searchString, return index
at the end, if we haven't found a match yet, return -1.
復制代碼
我也可以用一種更接近代碼的形式來寫:
for each index in sourceString,N = searchString.lengthPOSSIBLE_MATCH = sourceString[index to index+N]if POSSIBLE_MATCH === searchString:return index
return -1
復制代碼
你的偽代碼寫得有多像真正的代碼,你就會發現它有多么好用。
第四步:翻譯可以編碼的內容
注意:對于簡單的問題,這一步可以和上一步合并
到這時我們才第一次需要考慮語法、方法參數和語言規則的問題。可能你不是全部代碼都會寫,但是沒關系,先把會寫的寫出來。
function findFirstMatch(searchString, sourceString) {let length = searchString.length;for (let index = 0; index < sourceString.length; index++) {let possibleMatch = <the LENGTH chars starting at index i>if (possibleMatch === searchString) {return index;}}return -1;
}
復制代碼
注意我留了一部分沒有寫,這是故意的!我不確定JavaScript切分字符串的語法要怎么寫,所以我會在下一步查找它。
第五步:不要猜測
我發現所有新手程序員都會犯一個共同的錯誤,就是從網上找到一個方法,覺得“可能有用”,然后不經過測試就寫進代碼里。你不理解的代碼越多,就越不可能找到正確的方法。
你的程序的錯誤可能是你不了解代碼的兩倍還多。有一處不理解,如果程序出錯,那么罪魁禍首只有一處。如果你有兩處不理解,那就有三種可能出錯(A出錯,B出錯,或者A和B都出錯)。如果有三處不理解,就會有七種情況……很快它就失控了。
邊注:程序出錯的情況種類遵循Mersenne公式 a(n) = (2^n)?—?1
首先要測試你的新代碼。從互聯網上找答案是好的,但是在寫進你的代碼之前,你要先對它進行單獨的測試,確保它能按照你想要的方式執行。
在上一步中,我不確定JavaScript怎么切分字符串,所以我選擇面向Google編程
www.google.com/search?q=ho…
第一條結果來自w3schools,有點小過時,但比較可靠
www.w3schools.com/jsref/jsref…
基于此,我覺得我應該使用substr(index, searchString.length)
來提取sourceString
,但這只是個假設,所以我要先來測試一下:
>> let testStr = "abcdefghi"
>> let subStr = testStr.substr(3, 4); // simple, easy usage
>> console.log(subStr);
"defg"
>> subStr = testStr.substr(8, 5); // ask for more chars than exist
"i"
復制代碼
現在我確定這個函數是可以用的,如果程序出錯,就不是這個函數不可用導致的。最后我補充上最后的代碼。
function findFirstMatch(searchString, sourceString) {let length = searchString.length;for (let index = 0; index < sourceString.length; index++) {let possibleMatch = (sourceString.substr(index, length));if (possibleMatch === searchString) {return index;}}return -1;
}
復制代碼
結論
如果你讀到這里,我要說的只有:”干就完了!“
再嘗試處理一下上周遇到的困難,用上我教你的方法,我保證你很快就會有提高。
祝你好運,編碼愉快!
譯者注:個人認為作者還是強調要先想清楚,再動手寫代碼。而且要學會面向Google編程