本章描述了一个程序的基本构建方式
脚手架。最好的脚手架通常是最容易的脚手架。对某些任务来说,最简单的脚手架由一个使用Visual Basic、Java或Tcl之类的语言实现的图形用户界面构成。对于上述每一种语言,我都在半小时之内实现过具有点击控件和良好的图形输出的小程序。不过,对于许多算法任务而言,我发现更容易的办法是,摒弃这些强大的工具并使用我们在本章见过的更简单的命令行技术。
编码。对于比较难写的函数,我发现最容易的办法是使用方便的高级伪代码来构建程序框架,然后伪代码翻译成要实现的语言。
测试。在脚手架中对组件进行测试要比在大系统中更容易、更彻底。
调试。对隔离在其脚手架中的程序进行调试是很困难的,但是若将其嵌入真实运行环境中,调试工作会更困难。
计时。如果运行时间不重要,线性搜索要比二分搜索简单得多:许多程序员都可以在第一次实现的时候得到正确的代码。正是由于运行时间非常重要,我们才引入了更加复杂的二分搜索,所以,我们应该进行实验以确保程序能够达到我们预期的性能。
我对C/C++的编程感悟不多,js的比较多,大体说来就是变量命名,缩进等,在实际的开发环境中,保证团队代码风格的统一有利于提高生产率,也方便code review
js,node的repl和brower的console都很好用啊….动态语言还是喜欢的不行,lisp的IDE也挺好用的(Racket)思路的话就是一个递归
Mutation Testing(变异测试)
变异测试是通过选择一组变异算子,然后将它们一次一个地应用于源程序,用于每个适用的源代码片段。将一个突变算子应用于该程序的结果称为突变。如果测试套件能够检测到变化(即其中一个测试失败),则称该突变体被杀死。
例如,考虑下面的C ++代码片段:
如果 (a && b ) {
c = 1 ;
} else {
c = 0 ;
}
条件突变运营商将取代&&与||和产生以下突变:
如果 (a || b ) {
c = 1 ;
} else {
c = 0 ;
}
现在,为了杀死这个突变体的测试,应该满足以下三个条件:
测试必须达到变异陈述。
测试输入数据应该通过为突变体和原始程序产生不同的程序状态来感染程序状态。例如,一个测试,a = 1并b = 0会做到这一点。
不正确的程序状态(’c’的值)必须传播到程序的输出并由测试检查。
这些条件统称为RIP模型。[3]
弱变异检测(或弱变异覆盖)要求只满足第一和第二个条件。强烈的突变测试要求满足所有三个条件。强大的突变更强大,因为它确保测试套件能够真正发现问题。弱变异与代码覆盖方法密切相关。它需要更少的计算能力来确保测试套件满足弱突变测试而不是强突变测试。
但是,有些情况下无法找到能够杀死这个突变体的测试用例。结果程序在行为上与原始程序相同。这种突变体被称为等效突变体。
等价突变体检测是实际使用突变检测的最大障碍之一。即使对于小型程序,检查突变体是否等价也需要很高的努力。[13]克服等效突变问题的多种方法的系统文献综述(由[14]提出)确定了17种相关技术(在22篇文章中)和三类技术:检测(DEM); 建议(SEM); 并避免等效突变体生成(AEMG)。实验表明,一般的高阶变异和JudyDiffOp策略为等价突变问题提供了一种有前途的方法。
这里我会在调用代码的时候做个防御性断言。。。说白了就是不相信用户输入,写过模块的各位肯定也这么干过。
冒泡比较如果循序和索引相反就马上抛出异常,这个平均开销在n/2
要看什么测试,在前端测试中我如果要应用不太熟的技术我都会做几个demo测试,确保没有太大问题后再引用,测试驱动开发应该是件项目开发过程中比较稳妥的方式。
我能想到的是空间局部性?预读取?要看不同的kernel的吧….这个问题要挖下去有点深,暂且占个坑。
写前端的时候脚手架实在太多了….webpack,gulp,还有最近的percel….好没来及看机制,我对前端的GUI测试挺烦的,到目前为止还是手动编写测试用例肉眼测试的,后端还能跑测试框架,还请有好办法的朋友告知一声~
源码
#include
#include
#include
#define MAXN 1000000
typedef int DataType;
DataType x[MAXN];
int n;
/* Scaffolding */
int i = -999999;
#define assert(v) { if ((v) == 0) printf(" binarysearch bug %d %d\n", i, n); }
/* Alg 1: From Programming Pearls, Column 4: raw transliteration */
int binarysearch1(DataType t)
{ int l, u, m;
l = 0;
u = n-1;
for (;;) {
if (l > u)
return -1;
m = (l + u) / 2;
if (x[m] < t)
l = m+1;
else if (x[m] == t)
return m;
else /* x[m] > t */
u = m-1;
}
}
/* Alg 2: Make binarysearch1 more c-ish */
int binarysearch2(DataType t)
{ int l, u, m;
l = 0;
u = n-1;
while (l <= u) {
m = (l + u) / 2;
if (x[m] < t)
l = m+1;
else if (x[m] == t)
return m;
else /* x[m] > t */
u = m-1;
}
return -1;
}
/* Alg 3: From PP, Col 8 */
int binarysearch3(DataType t)
{ int l, u, m;
l = -1;
u = n;
while (l+1 != u) {
m = (l + u) / 2;
if (x[m] < t)
l = m;
else
u = m;
}
if (u >= n || x[u] != t)
return -1;
return u;
}
/* Alg 4: From PP, Col 9 */
int binarysearch4(DataType t)
{ int l, p;
if (n != 1000)
return binarysearch3(t);
l = -1;
if (x[511] < t) l = 1000 - 512;
if (x[l+256] < t) l += 256;
if (x[l+128] < t) l += 128;
if (x[l+64 ] < t) l += 64;
if (x[l+32 ] < t) l += 32;
if (x[l+16 ] < t) l += 16;
if (x[l+8 ] < t) l += 8;
if (x[l+4 ] < t) l += 4;
if (x[l+2 ] < t) l += 2;
if (x[l+1 ] < t) l += 1;
p = l+1;
if (p >= n || x[p] != t)
return -1;
return p;
}
/* Alg 9: Buggy, from Programming Pearls, Column 5 */
int sorted()
{ int i;
for (i = 0; i < n-1; i++)
if (x[i] > x[i+1])
return 0;
return 1;
}
int binarysearch9(DataType t)
{ int l, u, m;
/* int oldsize, size = n+1; */
l = 0;
u = n-1;
while (l <= u) {
/* oldsize = size;
size = u - l +1;
assert(size < oldsize); */
m = (l + u) / 2;
/* printf(" %d %d %d\n", l, m, u); */
if (x[m] < t)
l = m;
else if (x[m] > t)
u = m;
else {
/* assert(x[m] == t); */
return m;
}
}
/* assert(x[l] > t && x[u] < t); */
return -1;
}
/* Alg 21: Simple sequential search */
int seqsearch1(DataType t)
{ int i;
for (i = 0; i < n; i++)
if (x[i] == t)
return i;
return -1;
}
/* Alg 22: Faster sequential search: Sentinel */
int seqsearch2(DataType t)
{ int i;
DataType hold = x[n];
x[n] = t;
for (i = 0; ; i++)
if (x[i] == t)
break;
x[n] = hold;
if (i == n)
return -1;
else
return i;
}
/* Alg 23: Faster sequential search: loop unrolling */
int seqsearch3(DataType t)
{ int i;
DataType hold = x[n];
x[n] = t;
for (i = 0; ; i+=8) {
if (x[i] == t) { break; }
if (x[i+1] == t) { i += 1; break; }
if (x[i+2] == t) { i += 2; break; }
if (x[i+3] == t) { i += 3; break; }
if (x[i+4] == t) { i += 4; break; }
if (x[i+5] == t) { i += 5; break; }
if (x[i+6] == t) { i += 6; break; }
if (x[i+7] == t) { i += 7; break; }
}
x[n] = hold;
if (i == n)
return -1;
else
return i;
}
/* Scaffolding to probe one algorithm */
void probe1()
{ int i;
DataType t;
while (scanf("%d %d", &n, &t) != EOF) {
for (i = 0; i < n; i++)
x[i] = 10*i;
printf(" %d\n", binarysearch9(t));
}
}
/* Torture test one algorithm */
\#define s seqsearch3
void test(int maxn)
{ int i;
for (n = 0; n <= maxn; n++) {
printf("n=%d\n", n);
/* distinct elements (plus one at top) */
for (i = 0; i <= n; i++)
x[i] = 10*i;
for (i = 0; i < n; i++) {
assert(s(10*i) == i);
assert(s(10*i - 5) == -1);
}
assert(s(10*n - 5) == -1);
assert(s(10*n) == -1);
/* equal elements */
for (i = 0; i < n; i++)
x[i] = 10;
if (n == 0) {
assert(s(10) == -1);
} else {
assert(0 <= s(10) && s(10) < n);
}
assert(s(5) == -1);
assert(s(15) == -1);
}
}
/* Timing */
int p[MAXN];
void scramble(int n)
{ int i, j;
DataType t;
for (i = n-1; i > 0; i--) {
j = (RAND_MAX*rand() + rand()) % (i + 1);
t = p[i]; p[i] = p[j]; p[j] = t;
}
}
void timedriver()
{ int i, algnum, numtests, test, start, clicks;
while (scanf("%d %d %d", &algnum, &n, &numtests) != EOF) {
for (i = 0; i < n; i++)
x[i] = i;
for (i = 0; i < n; i++)
p[i] = i;
scramble(n);
start = clock();
for (test = 0; test < numtests; test++) {
for (i = 0; i < n; i++) {
switch (algnum) {
case 1: assert(binarysearch1(p[i]) == p[i]); break;
case 2: assert(binarysearch2(p[i]) == p[i]); break;
case 3: assert(binarysearch3(p[i]) == p[i]); break;
case 4: assert(binarysearch4(p[i]) == p[i]); break;
case 9: assert(binarysearch9(p[i]) == p[i]); break;
case 21: assert(seqsearch1(p[i]) == p[i]); break;
case 22: assert(seqsearch2(p[i]) == p[i]); break;
case 23: assert(seqsearch3(p[i]) == p[i]); break;
}
}
}
clicks = clock() - start;
printf("%d\t%d\t%d\t%d\t%g\n",
algnum, n, numtests, clicks,
1e9*clicks/((float) CLOCKS_PER_SEC*n*numtests));
}
}
/* Main */
int main()
{ /* probe1(); */
/* test(25); */
timedriver();
return 0;
}
分析:binarysearch1和2无非是先赋值后赋值的区别,3逐步逼近上界然后将查找成功判定提取到迭代外,4主要在第一个if 1000,9 边界出错,6Sentinel带来的控制流的减少,7loop unrolling (这个就是利用指令级的优化 )