1.程序是什么
程序是什么?程序在1940年代第二次世界大战中因为用计算机代替物理计算而出现,在80年的发展中,众多的计算机软件科学家给予了不同的定义。
1.1传统定义
程序是“数学表达式”。最早的程序是科学计算程序。由于量子力学的出现,在牛顿经典力学的基础上,物理学出现了新的分支,原子物理学,固体物理学,众所周知的晶体管是固体物理学发展的结果。新的学科需要很多的计算,决定了程序的数学性质。
程序的三种变量是程序的基础,有数字变量,字符变量和布尔变量。三种变量又组成表达式,布尔表达式的值是{true,false},然后表达式组成程序的语句。这种构成方式与数学的项,因式和计算表达式类似。
程序的语句有赋值语句(assignment),x=f(a,b,c),f(a,b,c)是表达式或者数学函数sinx,cosx(库函数),并且x是变量而a,b,c是常量,条件语句(conditional statement),if 布尔表达式B then C1 else C2,并且C1和C2是程序的语句,和循环语句(loop statement),while B {C1;}(C语言表示方法),布尔表达式B是循环实现迭代的条件。
程序的两个语句C1和C2,用C1;C2表示,称为程序的规格(specification)。条件语句的规格表示:
if B -> C1|C2, B为真,条件成立,则选择C1;B为假,选择C2。有用菱形代替“|”的表示方法。
循环语句的规格表示:
while B * C, B为真,则循环语句开始下一个迭代,否则循环结束到程序的下一个语句。*表示循环,与文法的闭包(closure)用法一样。
规格是数学思想,用严谨(rigorous)的方法表示程序。程序的数学性质代表人物有高德纳,霍尔和。高德纳是加州理工学院数学系的学生,开始写他的名著《计算机程序设计的艺术》,他把数学思想引导到计算机程序设计中,他认为不同学科的区别在科学思维的不同。在他看到巴克斯-诺尔范式以后,就认为数学的严谨能用在计算机程序设计中,因此他发明了LR(k)分析文法。计算机高级语言有很多,结构语言C,面向对象技术C++,函数式语言与逻辑式语言Lisp和Prolog,高级语言的困难不仅在语法的设计,更重要的是语言的编译器,LR(k)文法将程序语句的语法结构找到,判断语法是否正确。并且高德纳发明了属性文法,因此编译器不再困难。LR分析文法的精确仍然表示程序的数学性质。高德纳另一个更重要的发明是算法分析。他在加州理工,计算机公司和斯坦福大学工作,将高等数学的级数与概率论引到分析程序或算法的效率上,称为时间复杂度,区分一个问题的不同实现程序的性能的好坏。例如,一个程序有三层的循环语句,
while(){ /*第一层*/
for(){/*第二层*/
while(){...}/*第三层*/
}}
此程序的时间复杂度应是n的三次方O(n#3),n是输入数据的长度或大小。然而,若有一个程序能解决相同问题,只有一个两层的循环语句,因此时间复杂度是O(n#2),效率更好。因此,“高德纳教给我们区分不过如此和极其优秀。”
高德纳的学生陶尔扬,用Ackermann函数表示算法复杂度,Ackermann函数增长很快,反函数就增长很慢。在会计学的平摊amortization方法引到算法分析后,发明了动态数据结构的摊还分析,并且与他的学生斯里特使用物理的势函数表示算法复杂度。
霍尔是英国人,他在计算机公司工作时,发现程序在行数变多时,出现的错误数量是灾难。因此,他离开公司到英国的大学任教,后来去了剑桥大学。霍尔在计算机编程方面的重要贡献是霍尔逻辑,{P}C{R},P和R是数学表达式,表示语句C在执行前应遵守的条件和执行后的结果,称为断言(assertion)。霍尔逻辑用来实现程序语句的正确性证明,在程序分析和设计中得到广泛应用,例如程序验证(program verification)。
计算机程序设计经过了80年的发展,高级语言,编译器,商业软件,数据库,面向对象技术,分时操作系统,高性能计算机程序设计,并行程序设计,多核多线程,Windows微内核模式,Client/Server模式,开始处理复杂数据结构线性表,栈与队列,树与二叉树,图与邻接表,二叉搜索树,优先队列(堆),类与对象,动态树,map-reduce。数据结构的基本操作,例如队列的出队和入队操作,并不是数学表达式,高级语言的系统函数,例如strcpy函数仍然不是数学计算,因此计算机程序向多个方向发展,增加了字符处理函数,数据结构操作,类的封装与接口,并发编程,模式设计与组件,大数据等功能,因此程序的数学特征不再是一统天下。
1.2 数理逻辑定义
程序是逻辑。埃德蒙.克拉克的不动点分析,与模态逻辑(modal logic)。克拉克的逻辑发展到SAT求解机。模态逻辑不是逻辑式程序设计,可以用在各种程序中。
2.程序模式
2.1《代码阅读》第二章 基本编程元素
一、一个完整的程序
echo是UNIX系统上一个简单有用的程序,用在标准输出上(通常为屏幕)打印(显示)它的参数。
计算机的标准输入,标准输出,错误显示在程序设计中是stdin,stdout,stderr。”打印其参数“的意义是,echo向用户展示信息,是UNIX操作系统的一个命令,在命令行输入echo "this moment",将显示this moment,,因此“this moment"是echo的参数。
echo源程序取自NetBSD的upgrade脚本。库函数保存在头文件中,C和C++的程序应包含头文件,才能正确使用库函数,如同库函数是programmer自己编写的一样。C、C++与Java程序从main函数(在Java中叫方法)开始执行,windows、Java applet和serlet host、掌上电脑以及嵌入式系统,可能会使用另外的函数作为程序的入口,例如WinMain或init。
在C和C++程序中,main函数的两个参数argc与argv,将特定的命令行参数从操作系统传递到程序,变量argc制定参数的数量,argv是字符串数组,包含所有实参,程序名称(echo)位置为0,以NULL元素结束。C与C++程序中,典型的main函数定义为
int
main(int argc,char **argv)
而相应的Java类定义为
public static void main(string args[]){
program1. UNIX中的echo源程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main__P((int,char *[])); /*在函数声明中使用宏是为了向pre-ANSI隐藏参数*/
int
main(argc,argv)
int argc;
char *argv[];
{
int nfiag;
if(*++argv && !strcmp(*argv, "-n")){/*-n不是UNIX系统的换行符*/
++argv;
nflag=1;/* 参数指定了-n*/
}
else
nflag=0;
while (*argv) { /* 循环条件,将处理的参数不为空 */
(void)printf("&s",*argv);
if(*++argv) /* 若下一个字符串不为空*/
putchar(' '); /*输出一个空格分隔符*/
}
if(!nflag) /* 若没有指定-n,输出换行符*/
putchar('\n');
exit(0);
}
程序阅读:
1.程序的库函数有printf()和putchar(),显示字符串或者一个字符显示屏幕上。不仅能在操作系统的命令行输入字符串,UNIX系统的C程序中用对应的scanf()和getchar(),从键盘接收一个字符串或者一个字符。
2.程序用到一个复杂数据结构,指针数组*argv[],argv[i]是一个指针,指向一个字符串。因此,当编程时,对多个字符串进行处理,而且不等长,常用到指针数组实现存储和创立这些字符串。eg.dez
char *mstring[3]={“this”,“moment”,"\0"};
char *p=mstring[0];/*p指向mstring的第一个字符串*/
while ((*p)!="\0"){
printf("%s",*p);
p++;
}
exit(0);
3.argv的应用
echo与getopt并不兼容,因此指定输出不以换行符结束的-n,则-n参数需要程序员自己编写代码处理。首先,程序将argv移动到第一个位置argv[1],而不是argv[0]="echo",检验argv[1]参数是否存在,是否=-n,用strcmp()比较。在if的布尔表达式中,使用B1&&B2,则当B1=0时,系统将不再对B2求值。
4.库函数的返回值
printf()返回实际显示的字符的数量,转换为void是为了忽略该结果。putchar()在写入字符失败后返回EOF。所有的输出函数,在程序的标准输出stdout被重定向到一个文件时,可能由于各种原因而失败。
(1)存储输出的设备没有剩余空间。可能是输出缓冲区。
(2)设备上用户的空间配额可能已经耗尽。输出缓冲区满。
(3)(进程)试图向文件写入超过边界限制的数据。例如,超过系统文件长度。
(4)输出设备上可能发生硬件错误。
(5)一个文件的描述符或与标准输出相关联的流无法有效写入。写到标准输出的流无法写入文件。
不检查输出操作的结果,可能会导致程序悄然失败,在没有示警的情况下失去输出。
strcmp()的返回值,若两个字符串相等,则返回0(false)。
5.变量初始化
使用没有初始化的变量是导致问题的一个常见原因。nflag没有初始值,因此它的值是存储单元中的当前未知值。nflag被if-else-语句赋值为1或0,因此程序中并不使用此未知值。在检查程序时,应核实所有程序控制路径,在使用变量前,已经对变量正确初始化。
二、函数和全局变量
应灵活应用函数,如同if--else语句,while语句一样。
程序expand有一个有趣的地方,它的实现(implementation)使用了C语言中的所有控制语句。expand将参数指定的文件中的制表符(\t,ASCII码为9),展开成多个空格,默认是每8个字符设置成一个制表位。
2.2
主要用查德威克的《算法》第一章的程序模式。
程序
函数
API
数组
面向对象技术。 “抽象”数据类型
线性表
输入输出
2.3 编译器观念
在编译器看来,程序是基本块组成,一个基本块不会跳转到其他基本块中,而其他基本块也不会在这个块的中间跳转过来。一个基本块能包含多个语句,或者只有一个语句。程序的基本块之间的关系能用流图表示,或者用有向无环图表示。
3 数据结构
计算机程序的线性性。计算机存储器在逻辑视图上是线性特征,CPU执行对数据的操作也是线性方式,这决定了高级语言程序必须是线性的,才能在计算机上执行。现在不链接图灵可计算问题,应了解只有编写线性的程序,要编程的问题才会得到解决。程序的线性不是线性代数x+y ,而没有平方。程序的线性是数据结构的线性。图是many-to-many的逻辑关系,然而图算法的程序得到一个线性输出结构,或者过程是线性的,根据发现不同方面的图的线性性。例如输出图的最小生成树,最短路径问题是发现最短路径的SPT(最短路径树)。
4 程序功能
程序处理数据(data processing)的功能。
程序特征的章节组织方式:
第二章 按问题编程
第三章 程序结构和控制流与数据流
第四章 程序的正确性
第五章 用不变式推理程序
第六章 程序执行过程
第七章 程序功能结构
第八章 经典程序与算法
第九章 编程珠玑
第十章 集合论与文法
参考书是《编程珠玑》、《C语言高级程序设计》、《C++》、麻省理工《算法导论》、
《代码阅读》、查德威克《算法》,《编译原理》,
卢开澄《计算机算法导引--设计与分析》