不同編程即為不同解決問題的思路
解決一個問題有很多思路,比如:
- 過程式(C語言):將解決問題的方式分解成若干步驟來控制數據
- 面向對象式 (Java/Ruby):以對象為基本單位,定義其行為控制其內部狀態,通過不同對象之間的協作解決問題
- 函數式(Lisp):一切皆為函數,用連貫的思維方式定義過程,常用遞歸
- 組合式:將不同的解決方式組合起來,golang 經常會將面向對象與過稱式組合起來
示例: Huffman編碼
用 Golang 組合面向對象式和過程式:
首先我們應該確定整個 package (可視為一個大對象) 的輸入和輸出:
輸入: map[rune]int
, 其中 rune
是 golang 中的字符類型,也就是我們要進行編碼的數據類型,int
是這個字符出現的次數
輸出: map[rune]string
, 其中 string
對應的01字符串編碼結果。
定義數據結構
type Node struct {
Value rune
Weight int
LeftChild *Node
RightChild *Node
}
// Nodes 用于 Node 以 weight 排序
type Nodes []Node
// Tree 就是整棵Huffman樹的根節點
type Tree struct {
Root *Node
}
定義 package 的對外接口
func Encode(priorityMap map[rune]int) map[rune]string {
stortedNodes := makeSortedNodes(priorityMap)
hfmTree := makeFuffManTree(stortedNodes)
return hfmTree.encode()
}
具體實現思路
// makeSortedNodes 返回排好序的 []Node
func makeSortedNodes(priorityMap map[rune]int) []Node {
// 實現細節略
return hfmNodes
}
// makeFuffManTree 將排好序的 Nodes 構造成Huffman樹,
func makeFuffManTree(nodes Nodes) *Tree {
if len(nodes) < 2 {
panic("Must contain 2 or more emlments")
}
//實現細節略
return hfmTree
}
// encode 以 tree 根節點開始遍歷,得到最終編碼
func (tree Tree) encode() map[rune]string {
var initialCode string
encodeMap := make(map[rune]string)
tree.Root.traverse(initialCode, func(value rune, code string) {
encodeMap[value] = code
})
return encodeMap
}
// traverse 從當前節點開始向下遍歷,使用遞歸方式
func (n Node) traverse(code string, visit func(rune, string)) {
if leftNode := n.LeftChild; leftNode != nil {
leftNode.traverse(code+"0", visit)
} else {
visit(n.Value, code)
return
}
n.RightChild.traverse(code+"1", visit)
}
從上面的實現方式上可以看到,既有面向對象的 Node 和 Tree 也有過程式的 makeSortedNodes
和 makeFuffManTree
還有 func (n Node) traverse(code string, visit func(rune, string))
的遞歸調用,高階函數參數等。所以 golang 更加注重實用性。
這里僅僅從實現的角度定義了 Huffman編碼 的對外接口,其實還不夠通用和高效,參考 golang 的 io
package 我們可以將 Encode 做成 Encode(v []byte, w io.Writer) (int, error)
這樣就可以直接接入標準輸入輸出的package了,當然這里要做一些內部實現的調整。如果想要健壯和高效,還可以結合 buffer 實現緩沖。
項目完整代碼請進:github