最近算法课要求实现哈夫曼编码,由于前面的问题都是使用了F#来解决,偶然换成C#也十分古怪,报告也不好看,风格差太多。一开始是打算把C#版本的哈夫曼编码换用F#来写,结果写到一半就觉得日了狗了。。。毕竟FP水平图样,到处mutable,各种<-...于是想看看有没有现成的F#实现的哈夫曼编码。

F#的算法实现这种东西本身不好找,不过M$似乎有着预见性,得来全不费功夫。。。

原文

open System

/// 哈夫曼编码使用了一个叶子节点为输入符号,
/// 内部节点是他们所有符号组合的期望频率的
/// 二叉树。
type HuffmanTree =
| Leaf of char * float
| Node of float * HuffmanTree * HuffmanTree /// 为包含给定符号的字符串和期望的频率提供编码和解码
type HuffmanCoder(symbols: seq<char>, frequencies : seq<float>) = /// 从输入的频率构建一个哈夫曼编码树
let huffmanTreeLeafs =
Seq.zip symbols frequencies
|> Seq.toList
|> List.map Leaf /// 用于从哈夫曼编码树的节点获取频率
let frequency node =
match node with
| Leaf(_,p) -> p
| Node(p,_,_) -> p /// 从根节点列表构建一个哈夫曼编码树,遍历它直到唯一根节点
let rec buildCodeTree roots =
match roots |> List.sortBy frequency with
| [] -> failwith "Cannot build a Huffman Tree for no inputs"
| [node] -> node
| least::nextLeast::rest ->
let combinedFrequency = frequency least + frequency nextLeast
let newNode = Node(combinedFrequency, least, nextLeast)
buildCodeTree (newNode::rest) let tree = buildCodeTree huffmanTreeLeafs /// 为哈夫曼编码树的所有叶子构建哈夫曼编码表
let huffmanCodeTable =
let rec huffmanCodes tree =
match tree with
| Leaf (c,_) -> [(c, [])]
| Node (_, left, right) ->
let leftCodes = huffmanCodes left |> List.map (fun (c, code) -> (c, true::code))
let rightCodes = huffmanCodes right |> List.map (fun (c, code) -> (c, false::code))
List.append leftCodes rightCodes
huffmanCodes tree
|> List.map (fun (c,code) -> (c,List.toArray code))
|> Map.ofList /// 使用哈夫曼编码表编码字符串
let encode (str:string) =
let encodeChar c =
match huffmanCodeTable |> Map.tryFind c with
| Some bits -> bits
| None -> failwith "No frequency information provided for character '%A'" c
str.ToCharArray()
|> Array.map encodeChar
|> Array.concat /// 使用哈夫曼编码树将一个二进制数组解码为字符串
let decode bits =
let rec decodeInner bitsLeft treeNode result =
match bitsLeft, treeNode with
| [] , Node (_,_,_) -> failwith "Bits provided did not form a complete word"
| [] , Leaf (c,_) -> (c:: result) |> List.rev |> List.toArray
| _ , Leaf (c,_) -> decodeInner bitsLeft tree (c::result)
| b::rest , Node (_,l,r) -> if b
then decodeInner rest l result
else decodeInner rest r result
let bitsList = Array.toList bits
new String (decodeInner bitsList tree []) member coder.Encode source = encode source
member coder.Decode source = decode source

模式匹配##

模式匹配是F#中相当基本并且非常强大的特性。使用模式匹配可以让代码在清晰地表达其行为的同时更加简洁。上方所陈的每一个函数都使用到了模式匹配——亦是大量的F#典型代码。

简单说来,比如在huffmanCodes里,模式匹配使得其可以在可能出现的联合数据结构中轻松切换:

match tree with
| Leaf (c,_) -> //...
| Node (_, left, right) -> //...

更多复杂的例子(比如上面的decodeInner)不难发现模式匹配有助于引导你的代码。你例举了每一个你知道如何处理的情形,并且你所匹配的叶子节点暗示了数据需要在那种情形下定义。然后编译器将会热情地告诉你你有哪些没有覆盖到的情形。当我一开始撰写这个函数的时候,我就没有考虑到第一种情况,然后编译器告诉我

Warning: Incomplete pattern matches on this expression. The value '([],Node (_, _, _))' will not be matched

很明显对了!这个特定的输入指示了用户可能提供非法输入!

管道##

管道是一个用来描述声明一系列输入执行操作的不错的方式。这种哲学思想类似于在命令行里的管道——将左边的输入作为参数传递给右边。

因为F#库提供了一票很好的基本类型的操作用于处理你的数据,所以很容易通过管道描述这一系列的转换操作。例如,您可以很容易地声明筛选、映射、折叠、压缩或是重新包装数据。

集合##

代码使用了4种常用 F#/.NET 集合:

  • F# 列表:不可变链表,在一个用到了列表的递归算法里用到了。
  • F# 映射:不可变字典,用于存储每个符号。
  • F# 序列 = .NET "IEnumerable":基本集合的接口,用于输入。
  • .NET 数组:基本类型的数组用于输出编码。

值得注意的是,在集合间切换非常容易,使用诸如List.toArrayMap.ofList的函数就能轻松搞定。

“这是.NET的一部分!”又曰“华而不实”##

当我写这段代码的时候,我开启了实验模式,我仅仅在表层写了一些小的函数然后用F# Interactive来执行。当我觉得这个函数工作正常便想将所有的功能使用一个类来包装起来,然后给出一个漂亮的.NET接口,我就是这样做的:

  1. 把所有的内容拍到一个级别

  2. 把代码包裹起来:

    type HuffmanCoder(symbols : seq<char>, frequencies : seq<float>) = 
    
        // 要包裹的代码...
    
        member coder.Encode source = encode source
    member coder.Decode source = decode source

真是不能低估这神奇的能力!在F#里,从实验编码到零件设计编码的过渡简单而平稳。之所以F#可以这么来,是因为其是一个混合了函数式和面向对象的语言并且巧妙地被集成进了.NET。并且正是因为这个缘故,使用F#可以轻松构建大型.NET系统的组件。我听说有人对F#中的函数式面向对象批评曰其“华而不实”,但是我非常喜欢它,因为不论什么时候它都能让我纵享丝滑。

使用F#来实现哈夫曼编码吧的更多相关文章

  1. 哈夫曼(huffman)树和哈夫曼编码

    哈夫曼树 哈夫曼树也叫最优二叉树(哈夫曼树) 问题:什么是哈夫曼树? 例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80-89分: B,70-79分: C,60-69分: D,<60 ...

  2. (转载)哈夫曼编码(Huffman)

    转载自:click here 1.哈夫曼编码的起源: 哈夫曼编码是 1952 年由 David A. Huffman 提出的一种无损数据压缩的编码算法.哈夫曼编码先统计出每种字母在字符串里出现的频率, ...

  3. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  4. 赫夫曼\哈夫曼\霍夫曼编码 (Huffman Tree)

    哈夫曼树 给定n个权值作为n的叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的结点离 ...

  5. java 哈夫曼编码

    //哈夫曼树类 public class HaffmanTree { //最大权值 ; int nodeNum ; //叶子结点个数 public HaffmanTree(int n) { this. ...

  6. java实现哈夫曼编码

    java实现哈夫曼编码 哈夫曼树   既然是学习哈夫曼编码,我们首先需要知道什么是哈夫曼树:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫 ...

  7. 哈夫曼(Huffman)树和哈夫曼编码

    一.哈夫曼(Huffman)树和哈夫曼编码 1.哈夫曼树(Huffman)又称最优二叉树,是一类带权路径长度最短的树, 常用于信息检测. 定义: 结点间的路径长度:树中一个结点到另一个结点之间分支数目 ...

  8. 20172332 2017-2018-2 《程序设计与数据结构》Java哈夫曼编码实验--哈夫曼树的建立,编码与解码

    20172332 2017-2018-2 <程序设计与数据结构>Java哈夫曼编码实验--哈夫曼树的建立,编码与解码 哈夫曼树 1.路径和路径长度 在一棵树中,从一个结点往下可以达到的孩子 ...

  9. haffman哈夫曼编码的实现

    <span style="font-size:18px;">/* 1.在一棵二叉树中,我们定义从A节点到B节点所经过的分支序列为从A节点到B节点的路径: 定义从A节点到 ...

随机推荐

  1. android百度地图相关

    1.如果有报错Multiple dex files define Lcom/baidu/android/bbalbs/common/a/a一般是有重复jar包.    2.百度地图开发调试的应用程序正 ...

  2. Spring @RequestParam @RequestBody @PathVariable 等参数绑定注解详解

    背景 昨天一个人瞎倒腾spring boot,然后遇到了一点问题,所以把这个问题总结一下. 主要讲解request 数据到handler method 参数数据的绑定,所用到的注解和什么情形下使用. ...

  3. django TEMPLATES

    ?: (1_8.W001) The standalone TEMPLATE_* settings were deprecated in Django 1.8 and the TEMPLATES dic ...

  4. Linux系统编程重要细节记录(持续更新中)

    1.在打印rlim_t值时,需要将其转换为long long并使用%lld printf()修饰符.

  5. 怎么将oracle数据库的数据迁移

    打开要导出数据的PC,进入cmd界面 先进入数据库输入sqlplus,账号system密码508956.有权限的账户密码 2. create directory dir_555 as 'd:/asd1 ...

  6. Asp.net操作cookie大全

    实例代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 3 ...

  7. IIS 的一些配置记录

    1.日志分析: URL:http://www.cnblogs.com/fish-li/p/3139366.html2.性能监视: 执行 perfmon.msc ,右键添加counter,添加web s ...

  8. Spring.Scheduling.Quartz 作业的应用(定时任务和循环触发任务)

    .定时任务的实现,比如有个任务是要晚上2点10分的时候要去触发的,先定义这个任务类RskBookFilterInitDiningService.cs,这里其实有两种实现,一种是需要继承QuartzJo ...

  9. oracle for loop循环以及游标循环

    1. for in loop形式 DECLARE    CURSOR c_sal IS SELECT employee_id, first_name || last_name ename, salar ...

  10. sublime配置coffeeScript

    node.js 全局模块所在目录  npm -g ls 1.安装 npm install -g coffee-script 2.sublime安装CoffeeScript sublime语法高亮插件 ...