代码分析
源代码来源于Brian W. Kernighan 和 Dennis M. Ritchie 共同编著的书籍 《The C Program Language》 中1.5.4节中的单词计数。
中文版原文:这里对单词的定义比较宽裕,它是任何其中不含空格、制表符或换行符的字符序列,下面这段程序是UNIX系统中wc程序的骨干部分。
#include<stdio.h>
#define IN 1 /* 单词内 */
#define IN 0 /* 单词外 */
main()
{
int c, nl, nw, nc, state;
state = OUT; /* 未有任何字符被读必定为单词外 */
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '
')
++nc;
if ( c != ' ' && c != '
' && c !=' ' )
state = OUT;
else if (state == OUT){
state = IN;
++nw;
}
}
printf("%d %d %d
", nl, nw, nc);
}
以下为我的头脑风暴过程。
- 逻辑是什么?
- 读 字符
- 从标准输入流依次读取
在屏幕从左至右依次写入
- 判断 字符
- 单词和非单词
- 区分概念 (人脑逻辑)
- 单词内
- 单词外
- 无任何字符被读
- 非单词
- 区分标志 (使人脑逻辑映射到机器)
- 标志
IN
OUT
- 标志
- 区分概念 (人脑逻辑)
- 单词和非单词
- 读 字符
因为是依次从标准输入流读取字符,写入到屏幕显示出来的字符也是有顺序性。
我们的问题是模拟顺序的字符流移动过程中提取单词数。源代码中state
变量用作区分标志,state = IN
和 state = OUT
代表 单词内和单词外 ($单词外
eq 非单词 $)。有了区分标志,该如何模拟单词内->单词外
这种顺序的跨界过程?
标志变化 | 意义 | 编号 |
---|---|---|
OUT --> IN |
新单词 | 1 |
IN --> IN |
单词内 | 2 |
IN --> OUT |
新非单词 | 3 |
OUT --> OUT |
非新非单词->非单词 | 4 |
但随之产生了一个新的问题,OUT
具有二象性。
IN
情况下结果是唯一的,不用管。1逻辑
也就是新单词计数逻辑
不需要考虑OUT
情况,因为他们都是成立的。
但3、4逻辑
则必须考虑OUT
的二象性。要想让上面3、4逻辑
成立,需要额外条件约束,必须读入字符。这是个隐形条件,除了首次读取字符为非单词的特殊情况,OUT(无输入字符)-->OUT(有输入字符),
其他的OUT情况
都是处在有输入字符的情况中。
state = OUT; /* OUT(无输入字符) */
/*...省略....*/
if ( c != ' ' && c != '
' && c !=' ' ) /* OUT(有输入字符) */
state = OUT;
上述代码则是描述了 OUT(无输入字符)-->OUT(有输入字符)
这一特殊情况。
在这之后所有的OUT
都被限定为有OUT(有输入字符)
,通过如下循环限制。
while ((c = getchar()) != EOF){}
至此所有逻辑分析完毕,随之而然也可以得到 非单词计数
的逻辑,从而写出代码。下面是我的代码并经过实验验证。
int main(int argc, char *argv[]) {
int c, nl, nw, nc, state;
nl = nw = nc = 0; /* 各计数器初始化 */
state = OUT; /* 无输入时必定在单词外 */
while ( (c = getchar()) != EOF )
{
++nc;
if ( c == '
')
++nl;
if (c != ' ' && c != '
' && c !=' ')
{
state = IN;
}
else if ( state == IN ) /* 首个非单词 */
{
state = OUT;
++nw;
}
else if ( state == OUT ) /* 非首个非单词 */
{
state = OUT;
++nw;
}
}
printf("%d %d %d", nl, nw, nc);
return 0;
}
除去逻辑,仅仅说编程风格上,if语句
条件,改成常量在左,变量在右,更科学。《C编程专家》中有提到过。C语言规定常量不可以被赋值,如果少写了个=
号,编译的时候也会发现。而反之是不会提示的,因为语法上是没有错误的,但在我们逻辑上错误了。
else if( OUT == state )
另外对于新手来说,就是else if(){} 的逻辑问题了,else后面整个if语句是其执行语句
else
{
if(){
}
}
代码很简单,但却有许多值得思考的地方,也略窥大师的深厚功底,假设咱们坚持这样的10000小时理论,大家包括我都可以成为这个行业的专家。加油吧,与大家共勉。