以 Huffman coding 为例看 Golang 实用主义

不同编程即为不同解决问题的思路

解决一个问题有很多思路,比如:

  1. 过程式(C语言):将解决问题的方式分解成若干步骤来控制数据
  2. 面向对象式 (Java/Ruby):以对象为基本单位,定义其行为控制其内部状态,通过不同对象之间的协作解决问题
  3. 函数式(Lisp):一切皆为函数,用连贯的思维方式定义过程,常用递归
  4. 组合式:将不同的解决方式组合起来,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 也有过程式的 makeSortedNodesmakeFuffManTree 还有 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

相关阅读:以 Huffman coding 为例看函数式编程

你可能感兴趣的:(以 Huffman coding 为例看 Golang 实用主义)