计算单词的出现频率——设计

生气的散人 发布于 2013/02/18 17:12
阅读 591
收藏 7

在开发大型应用程序的时候,非常有必要将程序按照单元功能(模块) 分开。尽管这次的程序很小,但为了达到练习的目的,我们准备将这次的程 序模块化。

将word_count 程序分割成如图5-1 的样子。

[+]查看原图
图5-1 word_count 的模块结构

❶ 取得单词部分

从输入流(文件等)一个个地取得单词。

❷ 管理单词部分

管理单词。最终的结果输出功能也包含在这部分。

❸ 主处理过程

统一管理以上两个模块。

对于“取得单词部分”,我们完全可以直接使用1.3.6 节中的实现1

1好像本书的章节安排都 是有预谋的哦,哈哈!

get_word()中,对调用方输入的单词字符数做了限制,并且存在一些无 论如何都不允许的情况2。对于这一点,其实不必过于纠结,解决方案是提供 一个足够大的缓冲——1024 个字符的临时缓冲区。

2此时,可以考虑使用 4.2.4 节中介绍的方法。

对于如何定义“英语单词”,如果严密地去考虑,那就没完没了了,不妨 结合现成的get_word()中的实现——将使用C 的宏(ctype.h)isalnum()返 回真的连续的字符视作单词。

作为公开给其他部分的接口,“取得单词部分”提供了get_word.h(参照 代码清单5-1),在需要的时候,可以通过#include 引用这个头文件。

代码清单5-1 get_word.h

1: #ifndef GET_WORD_H_INCLUDED 2: #define GET_WORD_H_INCLUDED 3: #include <stdio.h> 4: 5: int get_word(char *buf, int size, FILE *stream); 6: 7: #endif /* GET_WORD_H_INCLUDED */

这次的主题是“管理单词部分”。

“管理单词部分”提供了4 个函数作为对外接口。

❶ 初始化

void word_initialize(void);

初始化“管理单词部分”。使用“管理单词部分”的一方,必须在一开始 就要调用word_initalize()。

❷ 单词的追加

void add_word(char *word);

向“管理单词部分”加入单词。

add_word()为传入的字符串动态地分配内存区域,并且将字符串保存在 其中。

❸ 输出单词的出现频率

void dump_word(FILE *fp);

将利用add_word()加入的单词按照字母顺序进行排序,并且向各单词追 加它们各自出现的次数(调用add_word()的次数),最后输出fp 指向的流。

❹ 结束处理

void word_finalize(void);

结束“单词管理部分”。一旦结束使用“单词管理部分”,需要调用 word_finalize()。

调用word_finalize()之后,再调用word_initalize(),管理单词部 分就回到最初的状态(清空了过去加入的单词)。

将这些整理到头文件如中,代码清单5-2 所示。

代码清单5-2 word_manage.h

1: #ifndef WORD_MANAGE_H_INCLUDED 2: #define WORD_MANAGE_H_INCLUDED 3: #include <stdio.h> 4: 5: void word_initialize(void); 6: void add_word(char *word); 7: void dump_word(FILE *fp); 8: void word_finalize(void); 9: 10: #endif /* WORD_MANAGE_H_INCLUDED */

在“主处理过程”中,使用get_word()努力地从输入流读取单词,然后 通过调用add_word(),将每次读取到的单词,不断地加入到“单词管理部分”, 最后再调用dump_word()将最终结果输出(参照代码清单5-3)。

代码清单5-3 main.c
[+]查看原图 [+]查看原图

代码清单5-3 中,特意将WORD_LEN_MAX 的值设定得大一些,但是无论如 何,这只是提供给临时缓冲的空间的大小。在add_word()内部,只是分配需 要的内存区域,并且将字符串复制进去,所以不会浪费太多的内存区域。

此外,考虑到例程的简洁,本章的程序省略了对所有的malloc()返回值 检查。

头文件的写法

写头文件的时候,必须遵守下面两个原则,

❶ 所有的头文件中,必须要有防止#include 重复的保护。

❷ 所有的头文件只#include 自己直接依赖的头文件。 “防止重复#include 的保护”是指,假设对于word_manage.h(参照代 码清单5-2)来说,

#ifndef WORD_MANAGE_H_INCLUDED #define WORD_MANAGE_H_INCLUDED : : #endif /* WORD_MANAGE_H_INCLUDED */

如果像上面这么做,即使当前头文件被重复#include,其中的内容也会被 无视,因此,就不会出现重复定义的错误。

下面来谈谈第2 个规则。假设对于头文件a.h,它依赖于头文件b.h(使 用了b.h 定义的类型或者宏等),因此在a.h 的开头#include 了b.h。比如, word_manage.h 使用了结构体FILE,所以word_manage.h 中#include 了 stdio.h。

很多人讨厌将#include 嵌套来写1,但在a.h 依赖b.h 的时候,如果不 采用嵌套,就需要在所有使用a.h 的地方都写成下面这样,

1作为C 的编程风格的 指导书,著名的《Indian Hill 编程风格手册》 (参照http://dennou-t.ms. u-tokyo.ac.jp/arch/comptech/cstyle/cstyle-ja.htm )中,也表达了“不要 用#include 嵌套”这 样的观点。可是,在附 属于C 处理环境的头 文件中,以及广泛使用 的免费软件中,随处可 以看到嵌套的头文件。 (第8 次印刷注:上面 的网页好像已经不存 在了,但在http://www. archive.org 中输入上 面的URL,应该可以 看到以前的影子)

#include "b.h" #include "a.h"

这样做是不是有点笨拙?不光每次写得很辛苦,而且将来某个时刻a.h 不再依赖b.h 后,上面的两行可能就永远地留在代码中了。另外,对于这种 #include 方式,开发现场的“可爱的”程序员们不可避免地会有下面的 举动,

唉~究竟是谁依赖谁,真是完全搞不明白哦!嗯~,先把已 经编译通过的.c 文件中开头的#include部分完全复制过来吧, 嘿嘿……

这样恶搞,肯定会在很多代码文件中留下大量无效的头文件引用。

啊?你问Makefile 的依赖关系是怎么做的?这必然是通过工具自动生 成的——这可是常识哦。

对于头文件,还有一个要点需要跟大家提前说明,那就是“应该把公 有的和私有的分开”这个原则,具体的内容会在后面给大家介绍。

要点
写头文件时必须遵守的原则:
❶ 所有的头文件中,必须要有防止重复#include 的保护。
❷ 所有的头文件只#include 自己直接依赖的头文件。

加载中
0
魔力猫
魔力猫
老兄劳驾排版一下好吗
0
Y-QTCe
Y-QTCe
排版已经很不错了,但是总有种写的太复杂了的感觉……
0
excepiton
excepiton
适合做毕业设计的文档,呵呵
0
生气的散人
生气的散人

本文摘自即在3月份上市的《征服C指针》

内容简介:
本书被称为日本最有营养的C参考书。作者是日本著名的“毒舌程序员”,其言辞犀利,观点鲜明,往往能让读者迅速领悟要领。 
书中结合了作者多年的编程经验和感悟,从C语言指针的概念讲起,通过实验一步一步地为我们解释了指针和数组、内存、数据结构的关系,展现了指针的常见用法,揭示了各种使用技巧。另外,还通过独特的方式教会我们怎样解读C语言那些让人“纠结”的声明语法,如何绕过C指针的陷阱。 
本书适合C语言中级学习者阅读,也可作为计算机专业学生学习C语言的参考。
-------------------------------------------------------------------------------------------
作者简介:
前桥和弥(Maebasi Kazuya) 
1969年出生,著有《彻底掌握C语言》、《Java之谜和陷阱》、《自己设计编程语言》等。其一针见血的“毒舌”文风和对编程语言深刻的见地受到广大读者的欢迎。 
作者主页:http://kmaebashi.com/。

0
cut
cut

设计文档写那么语言相关的做啥啊,还有你功能模块的实现一点都没写,就写了个接口的意义。

统计词频,一般不就一个最大四叉堆,使用词频数作为键值,轻松完成最大词频数O(1)的数据查找,写得那么复杂做啥子。

返回顶部
顶部