最近在看K&R的《The C programming language(2nd Edition)》 ,其中1.6节中有一道练习题Exercise 1-13,要求:Write a program to print a histogram of the length of word in its input.即按照给定输入,编写程序,输出单词长度的统计直方图。
分析:
1.首先想一下打印的直方图应该是什么样子的,按照要求,我们应该输出一个统计各个长度数量的直方图,即横坐标代表单词长度(1,2,3…),纵坐标代表频数,即各个单词长度的数量。为了得到这个直方图,我们需要知道各个长度的数量值,用数组表示这一值最为方便。为了打印,我们还需要知道该数组中的最大值。
2.如何统计各个单词长度的数量?首先利用getchar()函数对输入的文本流进行读取,之后判断该字符是空白字符(White space,即空格、制表符、换行符)还是非空字符(这里的单词是一个松散概念,即所有非空字符的序列),如果是空白字符就忽略,如果是非空字符就统计。注意,统计一个单词的长度需要设置阈值来标识这个字符是在单词中还是单词外。假设状态state有IN和OUT,初始值设为OUT,那么当第一次遇到一个非空字符时,应该把state设为IN,标识进入某个单词,之后循环累加;如果第一次遇到空白字符,那么将得到的长度对应的数组元素加1。
例子
先熟悉书上简单的例子。
#include <stdio.h>
/*count digits,white spaces, others*/
main(){
int c, i, nwhite, nother;
int ndigit[10];
nwhite = nother = 0;
for (i = 0; i < 10; ++i)
{
ndigit[i] = 0;
}
while ((c = getchar()) != EOF)
{
if (c >= '0' && c <= '9')
{
++ndigit[c-'0'];
}
else if (c == ' ' || c == '\n' || c == '\t'){
++nwhite;
}
else {
++nother;
}
}
printf("digits =");
for (i = 0; i < 10; ++i)
{
printf(" %d", ndigit[i]);
}
printf(", white space = %d, other = %d\n", nwhite, nother);
}
这段程序用来统计数字、空白字符和其他字符的个数,while循环内写得十分清楚。注意,在统计数字个数的时候,用数组存储’0’、’1’…’9’的个数,并且用ASCII码(c-‘0’)来计算对应的数值,因为对应数值0-9的ASCII码是连续递增的。
程序
有了上面的例子作为基础,可以进一步扩展写出以下程序。
#include <stdio.h>
#define MAXWORDLEN 10
#define IN 1 /*inside a word*/
#define OUT 0 /*outside a word*/
main()
{
int c;
int state = OUT;
long lengtharr[MAXWORDLEN + 1];
int wordlen = 0;
long thisval = 0;
long maxval = 0;
int thisidx = 0;
for (thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
{
lengtharr[thisidx] = 0;
}
while ((c = getchar()) != EOF)
{
if (c == ' '|| c == '\n' || c == '\t')
{
if(state == IN)
{
state = OUT;
if (wordlen <= MAXWORDLEN)
{
if (wordlen > 0)
{
thisval = ++lengtharr[wordlen -1];
if(thisval > maxval)
{
maxval = thisval;
}
}
}
else
{
thisval = ++lengtharr[MAXWORDLEN];
if (thisval > maxval)
{
maxval = thisval;
}
}
}
}
else
{
if (state == OUT)
{
wordlen = 0;
state = IN;
}
wordlen++;
}
}
for (thisval = maxval; thisval > 0; thisval--)
{
printf("%4d | ", thisval);
for (thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
{
if(lengtharr[thisidx] >= thisval)
{
printf("* ");
}
else
{
printf("%2s"," ");
}
}
printf("\n");
}
printf(" +-");
for (thisidx = 0; thisidx < MAXWORDLEN; thisidx++)
{
printf("---");
}
printf("\n");
printf("%7s", " ");
for (thisidx = 0; thisidx < MAXWORDLEN; thisidx++)
{
printf("%d", thisidx + 1);
printf(" ");
}
printf(">%d\n", MAXWORDLEN);
}
注:以上代码结构十分清楚,初始化后,while循环统计各个长度单词的数量,并得到最大的数量值。之后,循环打印,从最大的数量值开始打印,如果数组中对应的单词长度的值大于或等于这一值就打印,否则输出两个空格。打印完之后打印横坐标和横坐标标识。
REFERENCE
[1]: Brian W. Kernighan & Dennis M. Richie, The c programming language(2nd Edition)