高级语言程序设计方法学第02章-10
- 格式:pdf
- 大小:304.72 KB
- 文档页数:16
第2章 程序设计语言的设计概述本章介绍程序设计语言的设计目标以及为实现这些目标所遵循的设计准则,设计语言最后要给出语言的参考手册,为此,从表示的角度介绍与语法,语义和上下文约束有关的概念和表示法。
目前在程序语言语法文本中,语法形式表示是完整的,语义表示多半是非形式的。
本章仅给出各种形式语义的初步概念,有关理论后文还要讲述。
2.1 表示与抽象(Representation & Abstraction)程序就是程序设计语言表示的计算。
所谓表示是人为制造的符号组合以表达我们需要表达的意思。
每个合乎语法的表示都表达了某种语义,例如:float n; //n是C语言中的浮点数变量sqrt(n); //对n取平方根由于我们约定float是浮点类型的关键字,故float n这个合法表示表明“n是浮点数”, 是一个静态的说明。
同样,sqrt(n)说明要做“对n取平方根”的动作。
表示是对事物抽象的表达。
抽象是对论题本质的提取。
当我们为某软件投入市场作市场调查并写出分析报告时,我们已先完成了抽象(从社会效益、技术效益、经济效益列出定性或定量的分析),也完成了表示(报告正文)。
表示的规则是语法的,抽象的,内容是语义的。
同一事物可在不同抽象层次表示它。
同一程序可用高级语言表示、汇编码表示以及机器码表示。
当然,高级语言上面还可以有甚高级、超高级语言不断抽象上去。
所以,每种语言都是在某个抽象层次上的表示。
从日常概念中,我们可以得知概念愈是抽象其内涵愈小(只包含主要特征,略去其余)而外延愈大(能满足该主要特征的事物愈多)。
例如“张三在种地”也可以说“张三在改变生态环境”,还可以说“张三修理地球”都没有错。
之所以不错是表达了主要概念:“地表情况有了变化”。
但张三用锄种玉米的细节全被抽象(忽略)掉,且“李四砍树”也可以说成“改变生态环境”、“修理地球”。
当然,这种近乎失真的外延是不希望的,同样,能满足高级语言描述的计算的机器码实现也可以不止一个。
例如:给变量x增值1,x=x+1;这类赋值语句编译后的机器码可以有如下两种做法,按语义描述为:1. 从内存代表x的地址中取出值放在运算器中。
1. 从内存代表x的地址中取出值放在运算器中。
2. 加1,将结果放于某临时单元。
2. 加1将结果放入x地址中。
3. 将临时单元内容作类型检查(必要时转换),并放入x中。
x=x+1; x+=1;两者都对,但效率不同。
左边按赋值常规做法,保证赋值安全并没有错。
右边则认为编译时表达式x+1已作了类型检查,结果必然是x的类型,根据这个特殊情况节省了一步,C语言的 x+=1;的写法就是后者。
其它语言两者均可,完全由编译器决定了。
2.1.1 上层抽象可用多种下层抽象实现。
程序设计的四个世界。
客观世界的问题描述了客观世界对象相互之间的作用,经过分析建立解题模型。
read(m,s,d); mu=m-s-d u=m-s-d; s女儿8岁,儿子10岁 print(u) d母亲35岁.问: 每人每年增1岁 read(m,s,d); u和大于等于母亲? 一次,满足条 while(m+u>s+d+2u) u++; 指令集问题 模型 用程序抽象实现模型 实现程序客观世界 模型世界 程序世界 机器世界图2-1 计算机解题的四个世界一种模型只抽取与该层抽象有关的数据属性。
例如按图2-1的问题,儿子、女儿、母亲的姓名、身高体重、文化程度...... 与本题无关。
问题中的对象在本例中是三人年龄和要求解的年数。
数学模型是最高级的抽象,解题公式 u=m-s-d 即本题的数学模型。
程序对象可以是u,m,s,d,它直接和数学模型对象对应。
由于程序对象在程序世界有自己的行为,如输入/输出操作。
到计算机实现时又映射为存储对象和指令集。
它又要加上分配存储 、查找地址和往运算器、存储器和输入/出设备送数的操作(输入/出还要有数、码变换)。
所以 ,同一问题在问题世界,模型世界,程序世界,机器世界中对象及其相互作用是不同的 。
我们研究程序设计语言的设计是从问题—模型世界的需求出发 ,在机器世界可能实现情况下,定义自己的对象表示及其相互作用的表示。
对于从问题到程序,我们只介绍已有程序设计范型, 对于程序到实现,我们在以后章节将陆续分析程序各元素和各机制(即每种特征)以及它们的实现。
2.1.2 显式表示和隐式表示程序对象及其相互作用的表示可以是显式的。
如上例中的 float n,显式说明n是浮点变量;也可以是隐式的, 例如数组元素的次序(按下标序不可改); 一组连续的语句(语句次序不可改);程序中的缺省声明,如FORTRAN中符号名首字母为 1..N的程序对象自动为整型,其余为实型。
再如Ada中:for J in 1 .. 5 loop......end loop;其中J的类型自动为循环域(1..5)表示给出的整型。
一般说来,编译时,信息越多越准确,所以,强类型语言强调显式表示 。
但显式表示带来不必要的噜嗦,使程序不够精炼。
所以,在语义明确,不易出错之处,也采用隐式表示,例如Ada的常量声明:PI: constant FLOAT: =3.1415926;可写为:PI: constant : = 3.1415926;因为以3.1415926初始化时,等于声明了PI必须是浮点类型。
2.1.3聚合表示和分散表示程序对象、操作过程(或函数)在早期语言中为了快速翻译 ,只能用较原始的表示组合起来表示复杂的实体,或类似实体用不同的表示指明微小的差异,即分散表示。
例如,FORTRAN中没有记录数据结构,则将每个域定义为数组,各数组按同一序号取一元素构成一个记录。
再如,FORTH语言中不同类型“加”运算的分散表示:+ 单字长加号D+ 双字长加号M+ 混合加号其它语言中一个+号就概括了。
这叫聚合(Coherent)表示。
分散表示并不一定都坏。
例如,早期语言的函数(或过程)定义的型构(Signature)和体是不能分开的:FUNCTION SUM (V,N) //函数型构REAL V (N) // 局部变量SUM = 0.0DO 200 I = 1,N // 函数体SUM = SUM + V(I)200 RETURNEND而Ada等现代语言为上层表达方便,型构和体可以分开,只写:function SUM (V: VECTOR, N: INTEGER) return FLOAT;函数体可以写在别处,只要在联编时有一个完整定义的SUM函数存在,内部内联成一“内聚式”即可运行。
2.2 程序设计语言的设计目标设计程序设计语言即定义一组能表示某种范型的特征集 。
每个特征有严格的定义且可以在机器上实现。
程序员灵活运用这些特征便可表达他所希望任何计算。
在某种意义上说, 只要能想得出巧妙而高效的实现,就能提供更多更方便的特征 ,但就一个语言来说,这一组特征要能体现它的设计目标。
语言设计的目标在不同时代的软 、硬件背景下不能同一而语。
例如,早期语言追求时空效率,近代软件追求可靠性(易验证),可维护性(易读易改,改了不产生旁效),可移植性(尽可能远离具体机器)。
不同的应用范型也有不同的要求。
这里仅就当今最一般的目标,给出定性的描述:(1)模型能力(Model Power) 语言规定的语法特征应使程序员能充分、精确、容易地表达他的计算。
(2)语义清晰(Semantic Clarity) 每个合乎语法的表达式和程序都定义一个清晰、 无二义性的涵 义,且不因程序执行引起语义改变。
(3)实用性(Utility) 只设计使用频繁或没有它不行的特征,即每个特征都常用。
(4)方便性(Conveniency) 越是频繁使用的特征越要简便。
(5)简单性(Simplicity) 力求简单,一致,少生僻,除非十分方便不轻易增加冗余。
(6)可读性(Readability) 程序员往往从其特征定义的表达形式上理解其内涵,力求直接表达,“相当于”表达。
(7)可移植性(Portability)一种语言的程序在一般的机器上均可实现其计算,且不产生歧义。
(8)效率(Efficiency) 程序设计语言的效率指三个方面:编程效率;预处理和编译效率;执行效率 。
它们相互相关,对语言追求目标有不同的选择,总的希望都很高,但非常不易,只能折衷。
(9)灵活性(Flexibility)对语法以外的限定在不带来歧意的前提下允许放松。
这样能更好地发挥程序员的能动性并可简化实现。
C语言做得较好,如数组名也可以是指针,字符也可解释为整数等。
2.3 设计准则以上目标有时是相互冲突的,设计者只能按具体应用目标折衷。
为达到上述目标应注意以下准则:(1)频度(Frequency)准则频繁的特征应语义清晰、方便、简单、可读。
例如,if_then_else很频繁,结构化后更清晰。
(2) 结构一致(Structure Consistency)准则 一个程序的静态结构,直观上应与动态的计算结构一致,即表示结构与逻辑结构统一 。
这是E.W.Dijkstra提出的著名的准则, 并导致结构化程序普及。
违反这个原则,程序难以阅读、测试和维护,例如:100 IF(P) GO TO 150 while NOT P doA段 A段IF (EXP1) GO TO 110 if (EXP1) thenB段 C段GO TO 140 elsif (EXP2) then110 C段 D段IF(EXP2) GO TO 120 elsif ( EXP3) thenGO TO 140120 D段 E段IF (EXP3) GO TO 130 elseGO TO 140 B段endif130 E段 F段140 F段 end doGO TO 100 ... ...150 ... ...单看左边程序不花上10分钟很难理解程序是怎样执行的。
(3) 局部性(Locality)准则 一个模块(或函数、过程)只用自己声明的局部数据做一件事情。
这是保持语义清晰最有效的措施。
这个模块的修改不影响其它的部分。
近代程序小函数、小过程特别多,八件事调用八次,势必增加运行时调用界面上的类型检查,使运行速度降低,为此,语言设计上增加了内联(in_line)机制。
将函数代码插入到调用之处(插入时作类型检查)。
既保持了局部性,又保持了速度。
因为公用数据极易产生副作用(也称边界效应),造成程序错误,而且难以查错。
强行将公用数据局部化又会造成冗余和重复,逻辑上增加混乱。
故近代语言有不同层次控制数据公用措施,如堆变量、自动变量、静态变量、全局量等。
函数式语言全部采用局部量,Pascal、Ada、C有限制地使用全局量,BASIC全部是全局量。
(4)词法内聚(Lexical Coherence)准则 逻辑相关的代码表示上应相邻。