【譯】Googler如何解決程式設計問題

面向Google程式設計發表於2019-04-10

本文是Google工程師Steve Merritt的一篇部落格,向大家介紹他自己和身邊的同事解決程式設計問題的方法。

原文地址:blog.usejournal.com/how-a-googl…

在本文中,我將完整的向你介紹一種解決程式設計問題的策略,這個策略是我在日常工作中一直使用的,並且用它來幫助各個等級的程式設計師(包括新手、大學生和實習生)學習和成長。應用這個結構化流程可以最大幅度的減少那令人沮喪的除錯時間,並且能夠在儘可能短的時間內編寫出更加整潔、錯誤率更低的程式碼。

一步步

接下來我將用一個栗子來說明。

問題:有兩個字串sourceStringsearchString,返回searchStringsourceString中第一次出現的位置,如果searchString不是sourceString的字串,就返回-1。

第一步:畫下來

當你拿到一個需求,馬上就開始著手寫程式碼是一個非常愚蠢的主意。在寫一篇文章之前,首先要弄清楚論證和論據,並且你要保證論證是有意義的。如果你沒有這麼做的話,當你意識到你所寫的東西前言不搭後語時,你可能會因為浪費了大把的時間而想請自己吃一頓大嘴巴子。程式設計也是一樣的道理,甚至比這還嚴重,嚴重到像洗澡的時候把洗髮水弄進眼睛裡。

問題的解決方法通常很重要,即使它看上去很簡單。在寫程式碼之前,首先要做的就是把這個方法在紙面上呈現出來,並且保證在不同的情況下適用。

所以不找急著寫程式碼,甚至都不要思考如何寫。後面你會有充足的時間去敲程式碼,在這之前,你要把自己當成一臺計算機,弄清楚你這臺計算機會怎麼解決這個問題。

你可以使用流程圖,或者使用其他能幫你具象化的方法,總之我們的目標是解決問題。你可以用紙和筆隨意發揮,不需要收到鍵盤的限制。

我們從一些簡單的情況開始,如果一個方法是“輸入一個字串”,那麼“abc”可以作為第一個例子。首先要搞清楚正確的結果是什麼,然後去想怎麼樣得到正確的結果,並且一步一步的進行。

我們假設輸入的字串是這樣:

sourceString: "abcdyesefgh"
searchString: "yes"
複製程式碼

我的想法是這樣的:

我看到了searchStringsourceString裡,但是我要怎麼做呢?我從sourceString的第一個字元開始,逐字去讀,一直到最後,判斷每一個三個字元是不是yes。比如abcbcdcdy等等。當index值為4時,我找到了字串yes,所以我知道結果是index為4。

當我們寫下演算法時,必須要保證我們考慮了所有的情況,並且處理了所有可能的場景。當我們找到匹配的字串時,返回結果。如果找不到匹配的字串,同樣要返回結果。

我們來嘗試另一組字串:

sourceString: "abcdyefg"
searchString: "yes"
複製程式碼

我們重複剛才的操作,當我們讀到下標為4的字元時,找到的字串是yef,這是最接近的結果了,但是第三個字元卻不同,所以我們繼續往後讀,一直到最後,沒有找到匹配的字串,所以我們決定返回-1。

我們確定這一系列步驟(程式設計中,我們稱之為演算法)解決了我們的問題,並且處理了兩種不同的場景,每次都得到了正確的結果。這時,我們就對我們的演算法比較有信心了,並且可以將它形成條目。我們一起來進行下一步:

第二步:用英語寫下來

這裡我們考慮將第一步形成的演算法用英語寫下來。這可以使每一步變得更加具體,以便我們後面寫程式碼的時候有所參考。

  1. 從字串的第一個字元開始
  2. 檢視每一組3個的字元(其實是searchString的長度)
  3. 如果匹配上searchString,就返回當前的index
  4. 如果一直到末尾都沒有找到匹配的字串,就返回-1

看起來很棒,不是嗎

第三步:寫虛擬碼

虛擬碼並不是真正的程式碼,只是一種模擬形式,這裡我寫下上面的演算法的虛擬碼:

for each index in sourceString,
    there are N characters in searchString
    let N chars from index onward be called POSSIBLE_MATCH
    if 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.length
    POSSIBLE_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程式設計

相關文章