代码点评及解析:习题32,单词的长度(Word)

收到群里一位同学的代码,忍不住点评一下。也请各位指正。

话说《算法竞赛入门经典》P50有这样一道题,我给出如下比较详细的描述说明:

习题3-2,单词的长度(word).c
    输入若干个单词,输出他们的平均长度。单词只包含大写字母和小写字母,用一个或多个空格隔开。

如题所述,意味着用户输入的字符只有2种可能:字母、或是空格——连回车都不会有。这点一定要明确。不管是竞赛,还是自己练习,读懂题目(客户)的要求是很重要的。

依我之见,本题的解法策略不止一种。比如我给出的解法所采用的策略是:

说明:
    解决本题,需要用到字符串的知识。字符串,也就是一维的字符数组。
    结束输入的方法:Ctrl+Z,回车,回车。
策略:
    scanf("%s",...); 遇到空格时,会自动截断。很适合用在这种环境。
    strlen(); 用于计算字符串的长度
操作步骤:
    1、读入一个单词;
    2、计数器增量1
    3、计算单词长度,累加总长度
    4、总长度/计数器 ----> 平均长度

而群里面,那位同学给出的代码是这样的:

source1.c

0001 #include<stdio.h>
0002 #include<string.h>
0003 #include<ctype.h>
0004 #define A 100000+10  /*假定用户输入字符的最多个数*/
0005 int a[A];  /*存储用户输入的字符数据*/
0006 main()
0007 {
0008     int y=0,
0009         tot=0,  /*用户输入的字母的总数*/
0010         word=1;  /*单词的个数(这里初始化为1是不妥的,因为用户可能什么字母也没有输入,而只是输入了空格。*/
0011     char i=0;  
0012     gets(a);
0013     i=strlen(a);
0014     tot=i;
0015     for (;y<i;y++){
0016         if (isspace(a[y])) {  /*有关isspace()函数的用法,请参见:http://www.kuqin.com/clib/ctype/isspace.html 注意:用户的输入其实只包含空格和字母*/
0017             tot=tot-1;
0018             word=word+1;
0019         }
0020     }
0021     y=0;
0022     y=tot/word;
0023     printf("%d\n",y);    
0024     return 0;
0025 }

很遗憾,这位同学没有给出具体的解题思路说明,只是扔了一段代码(source1.c)上来,就说有错,要大家帮忙改正。说实话,他应该描述一下自己的思路,代码里面也加上一些必要的注释(上面的注释是我加上去的),说明一下为什么要这么写source1.c;这样一来,大家也好帮助他的。下面,我只好猜一猜他的思路了。

source1.c的策略是:

  1. 把用户的数据一下子都读入数组a中。int a[A]是错误的表达,应为char a[A].
  2. 原作者希望用i来表示用户输入的字符个数,可是却写出了char i=0;(第11行),这显然是错误的!正确的表达应为:int i; 这里的 i 也不必初始化为0,反正第13行都有给 i 赋值。
  3. 以y为数组的下标,从下标0开始,扫描整个数组a,分析其中的全部字母个数 tot 和单词个数 word。显然,这里的 tot 和 word 应为 int 类型。第14行到第20行的代码就是完成这项工作的。具体说来:原作者先假设所有的字符都是字母,故有第14行的 tot=i; 的假设,然后再扫描的过程中,每遇到一个空格,就将 tot 减量1。这部分是没有问题的。但是,很遗憾,原作者对于 word 的计算思路是错的:因为用户有可能输入许多连续的空格,所以不能一遇到空格,就认为增加了一个单词。那么,如何判断我们扫描到了一个新的单词呢?详见后面的叙述。
  4. 最后,原作者从第21行到第23行,用 y 来计算并输出了单词的平均长度。这里明显也存在着一个问题:y, tot, word 的类型都是 int ,这里单词的平均长度明显是存在小数的可能的。就如同计算班级学生的平均年龄一样:平均值,很多时候都是实数(带小数部分)。
  5. 要注意,isspace()函数所判断的“空白符号”指的是“空白符指空格、水平制表、垂直制表、换页、回车和换行符”这几种。注意:用户的输入其实只包含空格和字母

分析完了 source1.c 的诸多问题,让我们用原作者的思路来重写一下这部分代码吧。我给出的代码是这样的:

source2.c

0001 #include<stdio.h>
0002 #include<string.h>
0003 #include<ctype.h>
0004 #define A 100000+10  /*假定用户输入字符的最多个数*/ 
0005 main()
0006 {
0007     char a[A];  /*存储用户输入的字符数据*/ 
0008     int y,
0009         tot=0, /*用户输入的字母的总数*/ 
0010         word=0,  /*单词的个数*/
0011         i;
0012     gets(a);
0013     i=strlen(a);
0014     tot=i;
0015     for (y=0;y<i;y++){
0016         if (isspace(a[y])) tot=tot-1;
0017         if (
0018             (y==0 && !isspace(a[y]))   /*如果数组下标为0的元素不为空格,则算找到了一个单词。(数组最开始的地方,要特别处理。)*/
0019             ||
0020             (y>0 && isspace(a[y-1]) && !isspace(a[y]))  /*如果数组中某个下标比0大的元素不为空格,且该元素的前一个元素是空格,则算是找到了一个单词*/
0021         ) word=word+1;            
0022     }
0023     if (word==0) printf("没有输入任何单词");
0024     else printf("单词的平均长度是%f",(tot*1.0)/word);    
0025     return 0;
0026 }

关键问题说明:如何判断我们扫描到了一个新的单词呢?对应的代码是 source2.c 的第16~21行。

source2.c 的测试情况如下:

D:\temp>tcc -run test2.c
smart thanks
单词的平均长度是5.500000
D:\temp>tcc -run test2.c
      smart thanks
单词的平均长度是5.500000
D:\temp>tcc -run test2.c
  s mart t hanks
单词的平均长度是2.750000
D:\temp>tcc -run test2.c

没有输入任何单词

总结与反思:

  1. 解决问题之前,一定要搞懂问题本身的含义。她要我做什么?用户输入的数据是什么?我应该有怎样的输出?
  2. 用适合的数据类型来表达问题所涉及的数据。比如,用字符数组存储用户输入的字符串,用浮点类型存储平均值,用整型存储计数值…… 浮点类型和整数类型的选择,常常是初学者出错的地方。
  3. 要仔细考虑“边界情况”的处理:比如 source2.c 的第18行,就是对最开始的字符 a[0] 进行了特别的判定。请注意,数组下标从0开始,数组是没有 a[-1] 这样的元素的。
  4. 写代码的时候,一定要做好注释和说明工作。不然,自己看起来好累的,别人看起来好痛苦的!关于如何写注释,可以参考一下我的源代码。
  5. 多做测试。

至此,整个世界都清静了。

谢谢观赏!

原文地址:https://www.cnblogs.com/fzd19zx/p/1952890.html