Go channel 實現歸併排序中的 merge 函式

浮生若夢的程式設計發表於2018-01-29

最近學習 Go,但是苦於沒有專案練手,於是便逼迫自己:如果想到什麼有趣的東西,看能不能用 Go 實現一遍,於是便有了這篇流水文。

實現過程

歸併排序中的 merge 函式,相信每個人都很熟悉,網上隨便搜搜都有一大堆文章,這裡不再贅述細節。一開始,我用的是常規套路,不過覺得沒啥意思,無非是「換湯不換藥,感覺還是在拿自己熟悉的語言寫東西」。

聯想到 Go 的 channel 似乎能在某種程度上滿足我的要求,再加上 Goroutine 這種東西,便想:是不是也能利用利用這兩個語言特性。

channel 這個資料結構,在 Go 中有比較豐富的含義,但我基本上把它當佇列使用。Goroutine 也一樣,我基本把它等同於「使用者態執行緒」(兩者都很牛逼,不過作為應用層的開發者,有時候並不想深究太多,一切都往簡單方向理解)。

由於我只是練手,所以我想到的 API 長這樣:

Merge(ch1, ch2): outChan
複製程式碼

給定兩個有序的 channel,然後將其合併為一個有序的 channel。

於是我的實現如下:


func Merge(ch1 <-chan int, ch2 <-chan int) <-chan int {

    out := make(chan int)

    go func() {
        // 等上游的資料 (這裡有阻塞,和常規的阻塞佇列並無不同)
        v1, ok1 := <-ch1
        v2, ok2 := <-ch2
        
        // 取資料
        for ok1 || ok2 {
            if !ok2 || (ok1 && v1 <= v2) {
                // 取到最小值, 就推到 out 中
                out <- v1
                v1, ok1 = <-ch1
            } else {
                out <- v2
                v2, ok2 = <-ch2
            }
        }
        // 顯式關閉
        close(out)
    }()

    // 開完goroutine後, 主執行緒繼續執行, 不會阻塞
    return out
}
複製程式碼

使用 Go 的感受

語法近乎簡陋。不過對我而言並無大礙,反而喜歡。不太喜歡語法特性(語法糖)太多的語言,亂糟糟的,分散了太多注意力。要是每個語法特性都是相互正交的,我自然雙手贊同,但是如果多個特性都在做同一件事情,一般對學習者的負擔比較大的(Ruby),也不利於合作。

編譯型。不多講,幾乎已經是俺學習新語言的必要條件了,前期開發效率可能會慢點,但是換來的是更少的 bug,尤其是對團隊協作,可以減少很多痛苦。(要是你團隊裡面都是高手,用 Python 自然是爽歪歪,但是這一條件並不總是滿足。所以還是使用能編譯的語言吧 :))

相關文章