不同编程即为不同解决问题的思路
解决一个问题有很多思路,比如:
- 过程式(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