编程珠玑3-5
chapter 3 数据结构程序
"可用小程序的话就不要编写大程序"
"问题越一般化,解决起来可能也就越容易" ---Polya <How to Solve It>
直接解决一个23种情况的问题,要比编写一个处理n中情况的通用程序,然后将该程序应用到n=23时的情况更加困难。
程序员在对空间缺乏无能为力时,往往会脱离代码的纠缠,回头过去凝神考虑他的数据,这样会找到更好的方法,表示法是编程的精华。
几个原则:
将重复性的代码改写到数组中。使用最简单的数据结构-数组,来表示一段冗长的相类似的代码往往能达到最佳的效果。
封装复杂的结构。当你需要一个复杂的数据结构时,使用抽象的术语对它进行定义,并将那些操作表示成一个类。
尽可能的使用高级工具。超文本、名称-键值对、电子表格、数据库、语言以及类似的工具在其专门的问题领域内都属于功能强大的工具。
让数据去构造程序。使用适当的数据结构去替换复杂的代码。在编写代码之前,好的程序员会通篇理解构建程序时所围绕的输入数据结构、输出数据结构以及中间数据结构。
chapter 5
main.c 实现一个简单的二分查找例子
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define MAXN 15
typedef int DataType;
DataType x[MAXN];
int binarysearch(DataType t, int len)
/* pre condition: x[0]<=x[1]<=...<=x[n-1]
* post condition:
* result == -1 => t not present in x
* 0 <= result < n => x[result] == t
*/
{
int l, h, m;
l = 0;
h = len-1;
while(l<=h)
{
m = (l+h)/2;
printf(" %d %d %d \n", l, m, h);
if(x[m]<t) l = m + 1 ;
else if(x[m]==t)
{
//使用断言
assert(0<= m && m <len &&x[m]==t);
return m;
}
else h = m-1;
}
//当len为0时,h初始化为-1,此时会越界
assert((h<0 || x[h]<t) && (h+1>=len || x[h+1]>t));
return -1;
}
int main()
{
int i, n, t;
printf("input size of the arrary and key\n");
//用一个测试脚手架
while(scanf("%d %d",&n,&t)!=EOF)
{
for(i = 0; i<n; i++)
{
x[i]=10*i;
printf("%d\n",x[i]);
}
printf("%d\n",binarysearch(t,n));
}
return 0;
}
makefile:
OBJS = main.o
all: $(OBJS)
gcc -o all $(OBJS)
main.o: main.c
# tab键不能随便乱加,如果在一个依赖下(如该句下面)只是有一个tab,程序会出错。
#$@ 目标 $< 第一个依赖文件 $* 没有扩展名的目标名字
#“.PHONY”表示,clean是个伪目标文件。
#在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事
.PHONY: clean
clean:
-rm all $(OBJS)
脚手架 最好的脚手架就是那种最容易构建的脚手架。最简单的脚手架由一个图形用户界面组成,当然命令行简洁移植性更好。
编码 先使用便利的高级伪码为函数描述骨架,然后再转换为实现语言。
测试 在脚手架中测试组件要比在一个大系统中简单和全面的多。使用断言确保程序的正确性,同时符合逻辑和指定要求。输入、程序变量和输出之间的关系描述了程序的状态:断言可以精确的说明这些关系。例如用断言判断函数的前置条件和后置条件。
计时 确保预期的性能。
"work once twice":系统在第一次处理事务时都是正确的,但在后续的事务中都会出错。查找某个变量,这个变量在后续的事务中,是否被正确的初始化了。
无论在什么情况下,恰当的提问都会引导程序员找出bug:你站着和坐着的时候做了不同的事情吗?我可以看看你每次的登录方式吗?准确的说,推出程序之前你输入了什么?程序在开始失败之前正常工作过吗?有多少次?
本文使用Blog_Backup未注册版本导出,请到soft.pt42.com注册。