首页 > 代码库 > 第一章:引言
第一章:引言
1.1文学程序
文学程序(literate program):接口及其实现的代码与对其进行解释的正文交织在一起。文学程序由英文正文和带标签的程序代码块组成。例如,
1 〈compute x * y〉≡ 2 sum = 0; 3 for (i = 0; i < n; i++) 4 sum += x[i]*y[i];
定义了名为〈compute x * y〉的代码块,其代码计算了数组x和y的点积。在另一个代码块中使用该代码块时,直接引用即可:
1 〈function dotproduct〉≡ 2 int dotProduct(int x[], int y[], int n) { 3 int i, sum; 4 5 〈compute x o y〉 6 return sum; 7 }
文学程序可以按各个小片段的形式给出,并附以完备的文档。英文正文包含了传统的程序注释,这些并不受程序设计语言的注释规范的限制。
下面是文学编程系统的另一个特性,她有助于逐点对程序进行描述;下面以一个检测输入中相邻的相同单词的double程序,作为文学程序的例子:
先从定义根代码块来实现double,该代码块将使用对应于程序各个组件的其他代码块:
1 〈double.c 3〉≡ 2 〈includes 4〉 3 〈data 4〉 4 〈prototypes 4〉 5 〈functions 3〉
main函数处理double的参数。它会打开各个文件,并调用doubleword扫描文件:
1 〈functions 3〉≡ 2 int main(int argc, char *argv[]) { 3 int i; 4 5 for (i = 1; i < argc; i++) { 6 FILE *fp = fopen(argv[i], "r"); 7 if (fp == NULL) { 8 fprintf(stderr, "%s: can‘t open ‘%s‘ (%s)\n", 9 argv[0], argv[i], strerror(errno)); 10 return EXIT_FAILURE; 11 } else { 12 doubleword(argv[i], fp); 13 fclose(fp); 14 } 15 } 16 if (argc == 1) doubleword(NULL, stdin); 17 return EXIT_SUCCESS; 18 } 19 20 〈includes 4〉≡ 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <errno.h>
getword从打开的文件读取下一个单词,复制到buf [0..size 1]中,并返回1;在到达文件末尾时该函数返回0。
1 functions 3〉+≡ 2 int getword(FILE *fp, char *buf, int size) { 3 int c; 4 5 c = getc(fp); 6 〈scan forward to a nonspace character or EOF 5〉 7 〈copy the word into buf[0..size-1] 5〉 8 if (c != EOF) 9 ungetc(c, fp); 10 return〈found a word? 5〉; 11 } 12 13 〈prototypes 4〉≡ 14 int getword(FILE *, char *, int);
getword除了从输入获取下一个单词之外,每当遇到一个换行字符时都对linenum加1。doubleword输出时将使用linenum。
1 〈data 4〉≡ 2 int linenum; 3 4 〈scan forward to a nonspace character or EOF 5〉≡ 5 for ( ; c != EOF && isspace(c); c = getc(fp)) 6 if (c == ‘\n‘) 7 linenum++; 8 9 〈includes 4〉+≡ 10 #include <ctype.h>
size的值限制了getword所能存储的单词的长度,getword函数会丢弃过多的字符并将大写字母转换为小写:
1 〈copy the word into buf[0..size-1] 5〉≡ 2 { 3 int i = 0; 4 for ( ; c != EOF && !isspace(c); c = getc(fp)) 5 if (i < size - 1) 6 buf[i++] = tolower(c); 7 if (i < size) 8 buf[i] = ‘\0‘; 9 }
剩下的代码逻辑是,如果buf中保存了一个单词则返回1,否则返回0:
1 〈found a word? 5〉≡ 2 buf[0] != ‘\0‘
doubleword读取各个单词,并将其与前一个单词比较,发现重复时输出。它只查看以字母开头的单词:
1 〈functions 3〉+≡ 2 void doubleword(char *name, FILE *fp) { 3 char prev[128], word[128]; 4 5 linenum = 1; 6 prev[0] = ‘\0‘; 7 while (getword(fp, word, sizeof(word)) { 8 if (isalpha(word[0]) && strcmp(prev, word)==0) 9 〈word is a duplicate 6〉 10 strcpy(prev, word); 11 } 12 } 13 〈prototypes 4〉+≡ 14 15 void doubleword(char *, FILE *); 16 17 〈includes 4〉+≡ 18 #include <string.h>
输出是很容易的,但仅当name不为NULL时才输出文件名及后接的冒号:
1 〈word is a duplicate 6〉≡ 2 { 3 if (name) 4 printf("%s:", name); 5 printf("%d: %s\n", linenum, word); 6 }
总结:整个程序的设计符合学习C语言经常提的:自顶向下,逐步细化,模块化!