第三章过程式程序设计语言.ppt

上传人:sccc 文档编号:5293902 上传时间:2023-06-23 格式:PPT 页数:94 大小:761.02KB
返回 下载 相关 举报
第三章过程式程序设计语言.ppt_第1页
第1页 / 共94页
第三章过程式程序设计语言.ppt_第2页
第2页 / 共94页
第三章过程式程序设计语言.ppt_第3页
第3页 / 共94页
第三章过程式程序设计语言.ppt_第4页
第4页 / 共94页
第三章过程式程序设计语言.ppt_第5页
第5页 / 共94页
点击查看更多>>
资源描述

《第三章过程式程序设计语言.ppt》由会员分享,可在线阅读,更多相关《第三章过程式程序设计语言.ppt(94页珍藏版)》请在三一办公上搜索。

1、第三章 过程式程序设计语言,基本观点:计算实现的模型如果按冯诺依曼原理强制改变内存中的值叫命令(或译指令、强制Imperative式)的。由于强制改变值,程序状态的变化没有一定规则,程序大了就很难查错,很难调试,不易证明其正确。组织程序的范型即:算法过程+数据结构(计算控制+计算对象)3.1 计算对象表示值与类型3.2 计算对象实现存储3.3 计算对象连接束定3.4 计算组织-程序控制3.5 计算组织-函数与过程3.6 计算组织-抽象与封装,类型是计算机可能实现的结构和约定对客观世界差异的刻划。同一类型的外延,即同一结构表示所有可能的值构成一个域。分类原则:同样表示结构,同样语义解释,同样的操

2、作。同类型值运算结果:同类型。无符号整数:二进制解释的值整数:符号 值,3.1 计算对象-值与类型,浮点数:符号 阶码 尾数,程序语言中:基元(primitive)类型:整型/实(定,浮)/字符/真值/枚举结构(structured)类型:元组/数组/记录(结构)/表/串,3.1.1 字面量、变量、常量,名字操纵值,名:字面量 从名字即知类型 字面值不变 变量 符号名要声明类型 值可变 常量 符号名要声明类型 值不变 基元值的名字是地址的别名,地址值在计算机中不恒定 操纵地址的名字是指针(地址变量)float*p,x=37.32;p=,(p)203C,(x)117F,117F,37.32,续,

3、名值可分导致 x=x+1;按名 按名取值:引用reference 存值 左值 右值 int x 既是右值也是左值,3.1.2 值是头等程序对象,程序语言中的值字面量(整、实、布尔、字符、枚举、串)复合量(记录、数组、元组、结构、表、联合、集合、文件)指针值变量引用(左值、右值)函数和过程抽象,数学对象参与运算的权利是一样的,值是计算对象也要按一致性原则:可出现在表达式中并求值可作函数返回值可单独存储 可以构成复杂的数据结构可作函数参数,3.1.3 类型系统,类型定义 值的集合和值上操作集合(V,Op)类型系统 一组可直接使用的类型类型规则类型检查机制,3.1.3 类型系统,静态与动态 静 动

4、变量 有类型 无类型 动态简洁、灵活 参数 有类型 无类型 静态清晰、死板 值 有类型 有类型弱/强类型无类型 LISP,Smalltalk弱类型 变量有类型。类型兼容性大,系统不作检查强制类型 隐式类型强制(转换),自动截尾,补零。显式 类型强制 PL/1伪强类型 静态均有类型且作检查,由于不严,导出等价准则 Pascal强类型 类型有严格定义,均作检查 Ada,类型等价 按结构等价 type A is array(range 1.100)of INTEGER;type B is array(range 1.100)of INTEGER;OA1,0A2:A;OB1,OB2:B;OC1:arr

5、ay(range 1.100)of INTEGER;OD1,OD2:array(range 1.100)of INTEGER;OE1:A;OA1,OA2,OB1,OB2,OC1,OD1,OD2,OE1均等价,续,按名等价 OA1,OA2 是同一类型(都用A声明)OA1,OB1,OC1是不同类型(类型名为A,B,无)OD1,OD2 是同一类型(同时声明,虽无名)OD1,OC1 是不同类型(两次声明)OA1,OE1 是同一类型(虽两次声明,但同名)类型完整性准则 涉及值的类型中不能随意限定操作,力求没有第二类的值,续,3.1.4 类型兼容,不同类型值混合运算,人为定出计算级别,由低 层升格为高层,

6、结果值是高层的隐式转换 弱类型 I:=R;显式转换 强类型 I:=Integer(R);强类型按名判定,不同类型名则不兼容只有子类型不同名可以兼容显式和隐式混合,type BASE is INTEGER;subtype SON_TYPE is BASE range 1.1000;-子类型type DIVERSE is new BASE range 1.1000;-派生类型A,B:BASE;C,D:SON_TYPE;E:DIVERSE;A:=B+C,-合法,结果为B类型赋给AA:=C+E;-不合法A:=C+SON_TYPE(E);-合法,有显式强制A:=E;-不合法,两个类型E:=B+BASE(

7、E);-不合法,续,3.2 计算对象的实现-存 储,存储对象是程序对象在计算机中的实现 程序对象不一一对应为存储对象 x:=0;x,0是两程序对象 只有一个存储对象x加指令清零 初值常量也不作为单独存储对象,3.2.1 程序变量的时空特性,引用和指针,P指针是地址变量*P是P所指的内容,也有左值和右值*P左值是P所指地址值,即P的值*P右值是所指地址内的内容值,引用是常指针是变量的别名,但实现是不一样的,递引用 dereference,通过指针变量引用变量的值为递引用*P1右值即递引用有些语言显式递引用算符如FORTH的,1 13 VARIABLE xx(声明变量xx并赋初值13)2 0 VA

8、RIABLE Y(声明变量Y并赋初值0)3 xx 2*Y!(相当于Y=xx*2)如果只写xx 2*则为将xx的地址乘以2放在Y之中,3.2.1 变量的时态,分配/未分配/除分配分配:为程序对象创建存储对象 编译时分配叫静态分配 allocate 运行时分配叫动态分配如声明指针p,执new才分配未分配:声明了未分配运行时分配除分配:取消存储对象(程序对象)delete操作显式自动除配:无用单元收集Garbage collection动态语言有,静态可有Ada可没有C,续,4,3,?,?,a b c d e f,声明和定义:定义必然声明;反之不然 声明的两个作用:给出对象,该对象的时间有效性 出了

9、声明的作用域该对象失去定义。在声明的作用 域内显式删除也失去定义,定义/未定义/失去定义 只要分配存储对象必然有残值,无意义即未定义 赋值或初值变量得以定义,a,b:分配且有定义c,d:分配未定义或失去定义e,f:未分配或除配,3.2.2 存储模型,基元类型值 仅除数组记录、构造、表 不可更新其中一元素函数抽象,ML重过程变量引用,可存储值Storable:指最小的可直接访问的可存储单元中的值Pascal可存储值:集合不选择更新某一元素是可存储值,Pascal,C,Ada数组可选择更新,不是可存储值。引用非可存储(C+可存储),过程和函数名也非可存储ML几乎都是可存储值,也带来毛病:每次更新结

10、构数据都要重来。它们是:,(if exp then sin else cos)(x)得sin(x)cos(x),可存储值,存储对象的生存期,全局变量 和引用程序寿命一样长局部变量 和程序中的一个模块寿命一样长持久变量 比程序寿命长除非显式撤销 文件变量瞬间变量(transient)持久变量的逆,每个存储对象都有创建(生),可用(活着),撤销(死)的生命期。按生命期长短分:,静态存储对象,编译时分配存储对象,近代语言类属对象直到装入后确立(elaboration)之时才定下存储对象叫静态分配 一旦执行不再改动其存储,直至所在存储单元无效叫静态(Static)存储对象 全局变量均为隐式的静态对象,

11、COBOL,BASIC全静态,ALGOL,C是显示声明静态,Pascal除全局,Ada 不能。C语言的静态变量是既私有又不随所在声明块中消逝,全局于所在文件。auto是静态分配动态装入不叫静态对象。Extern是静态对象。,extern,static,auto,动态存储对象,把寿命特长的(如文件,全局量)排出来归到栈底的某一组,把寿命特短的(如循环控制变量)另立嵌套组,这个问题也就解决。,块5,块66,块1,块2,块3,块4,5,4,6,6,5,4,6,寿命,动态存储对象,二叉树其大小由输入值定在运行中确立。内存开辟堆(heap)随生成随堆放动态存储对象。指针(即堆变量)所指程序对象用堆存放,

12、问题:多次重分,内存成了小洞 解决办法:按寿命分组寿命最长的放在较低(按其所在块生命期)。,重复使用,无法再分,堆栈帧管理,由动态堆栈联想到一般嵌套式语言可按动态堆栈式管理,多数变量和所在块寿命一样长(语言称之为自动变量),动态堆栈式存储 按程序动态执行,以动态堆栈管理局部数据和动态生成数据 运行时堆栈Run-time stack其底压入程序代码和全局,静态量。每执行到调用时生成一个堆栈帧(frame)记录该块数据信息,每当返回则局部量自动撤销对于递归块的局部量可多次生成多次消除。动态链描述调用父辈地址,返回地址是继续执行的下一地址。静态链描述词法父辈lexical parent块地址,按该块

13、复制局部变量。,参数,返回地址,动态链,静态链,返回值,局部变量,程序代码全局静态存储,首先调用块堆栈帧,第二调用块堆栈帧,最新调用块堆栈帧,临时变量空间,栈顶,堆栈帧组织,运行时堆栈,续,调用块首地址,本帧词法父辈,举例 求整数连乘积之递归程序:function product(jj:Integer):Integer;var kk:Integer;begin if jj=0 then product:=1 else begin readln(kk);product:=kk*product(jj-1)end end;,Product函数目标代码,jj:2return:?kk:25,jj:1re

14、turn:?kk:7,jj:0return:?kk:?,临时存储,DL,SL:静态链,DL:静态链,最初调用时堆栈帧,第一次调用时堆栈帧,第一次调用时堆栈帧,栈顶,SL,动态堆存储,忽略死对象 不超过一页浪费,若寿命差不多浪费不大保持一个自由表(链表)8个字节头说明数据按类型长度保持多个表减少识别域开销(Ada),动态堆栈缺点:开始帧不知有多大,要求存储对象比创建它的块寿命长。指针和显式动态分配依然不少了堆。按帧也设heap 各种语言堆分配 FORTH LISP C Pascal Ada分配 Here cons malloc new new除配 无回收 有回收 显式除 显式除 有回收 死堆对象

15、处理 死堆对象也叫无用单元garbage。(垃圾),3.2.3 悬挂引用Dangling Reference,当堆式管理同时提供显式除配命令KILL时;堆栈式管理外块指针指向内块对象时;函数抽象作为第一类值时,都会产生悬挂引用 解决办法 Pascal把指针限制为堆对象且不用dispose,不提供地址运算。操作数组不能按指针寻址,快速索引 C语言比较自由,悬挂引用留给程序员 局部函数作为返回值产生的悬挂指针。ML,Mirada完全作为堆变量且无KILL。Algol 68是引用(常指针),不赋比局部量寿命更长的值,例 悬挂引用(C)int*dangle(int*ppp)/参数是指针的指针 int

16、p=5;int m=21;*ppp=&p;/传回的指向p的指针 return&m;/返回m的地址main()int k=17;int*pm,*pk=&k;/pk指向k pm=dangle(&pk);/返回时pm,Pk均指向已/失去定义的指针函数的/局部量,即p和m,3.3 计算对象的连接-束定Binding,名字操纵程序对象。名字和存储对象联系起来才成为程序对象。把声明名字(地址)和存储对象或语义实体连接起来叫束定。束定 绑定 定连 连编(编译中连接模块)名字与束定 一名可束定到多个对象。一对象可以束定到多个名字。在一个程序的声明期内一旦束定不再改变叫静态(早)束定 运行中一个名字束定到(多个

17、)对象叫动态(晚)束定,3.3.1 静态束定,编译按数据类型为名字分配合适大小的存储对象,该对象首地址填入符号表即为静态束定,编译后变量表销毁,故静态束定不能变。,符号表 运行时内存,类型 名字 束定 存储对象,real length(地址),array1.4 of integer age(地址),多重束定 FORTRAN等价语句,以内部引用实现。Real P2,P3Dimension P1(3)Equivalence(P1,P2),(P1(2),P3),P1 P2 P3,其它语言多重束定是:COBOL REDEFINES Pascal 变体记录 C 联合,3.3.2 动态束定,运行时将名(字

18、)束定于其体(得 其语义操作)解释型语言一般保留名字表运行中束定。FORTH语言动态束定它是编译 解释型每当生成word出一字项,有两片代码 一为初始化和该名应执行的代码(编 译完成)。新字体中用到旧字,则运行中束定。解释执行另一片代码时,动态束定编译块。FORGET命令可以撤销当前字定义。,名字域(字),链接域,类型域,参数域(体),.,WORD,WORD,项,项,项,指前一字指针,执行堆栈,例 FORTH 的动态束定0:2by3array(:表示编译开始,后为类型声明符)1 create(编译动作:将类型声明符装入字典项)2 2,3(在字典项中存入 23维数)3 12 allot(为六个短

19、整数分配12个字节)4 does(运行时动作指令,取下标)5 rangecheck(函数调用,检查下标)6 if(如果不越界)7 linearsub(函数调用,计算线性下标值)8 then(给出数组基地址和位移)9;(;切换成解释执行,数据类型定义毕)10 11 2by3array box(声明并分配名为box的数组变量)12 10 1 2 box(给box(1,2)赋值10),3.3.3 无类型语言束定,名字 束定,length,age,符号表 运行时内存存储对象:类型标签,scalar number,array of 4 number,不同时刻可以将一名字束定到不同存储对象上。,APL语言

20、可显式操纵束定:束定符号 APL是无类型解释执行语言,以下是计算税金的程序:TAXCALC 1 ENTER GROSS PAY 2 GROSS/输入什么值GROSS就是 什么类型,表示终端输入。3 LESS GROSS 18000/条件表达式为真转 LESS标号句 4 TAX.25 GROSS/表达式结果值束定到TAX 5 DISPLAY/转到标号DISPLAY句 6 LESS:TAX.22 GROSS 7 DISPLAY:THE TAX IS$,TAX 8,3.3.4 声明declaration,声明指明本程序用到的所有程序对象给出所有束定集合 声明给翻译必要信息 声明的确立产生事实上的束定

21、 声明有显式的和隐式的 例:Ada的显式声明:基本声明:=对象|数|类型|子类型|包|子程序|任务|类属|异常|类属设例|换名|延迟Ada的隐式声明:块名字,循环名字,语句标号,循环控制变量,预定义运算符,声明种类,定义,为对象名提供完整的束定信息。声明可多次定义只次。定义是从已有信息定义新信息,Ada称之为rename。函数式语言,无类型语言只有值定义,没有变量声明。,val even=fn(n:int)=(n mod 2=0)函数型构 函数体 束定 函数定义 值定义 fun even(n:int)=(n mod 2=0)函数抽象 体,顺序声明的Ada例子,package MANAGER i

22、s-声明程序包规格说明 type PASSWORD is private;-声明私有类型未定义 NULL_PASSWORD:coustant PASSWORD;-立即用私有类型声明变量 function GET return PASSWORD;-返回私有类型函数 function IS_VALID(P:in PASSWORD)return BOOLEAN;Private type PASSWORD is range 0.700;-定义私有类型 NULL_PASSWORD:constant PASSWORD:=0;-此时才定义end MANAGER;,顺序与并行声明,声明是顺序的,声明后立即有用

23、,次序是重要的 D1;D2;D3/D2中可利用D1的声明,D3可利用D1,/D2的声明(见Ada例)并行声明D1D2它们一般相互独立,确立次序不影响语义 ML的例子:Val Pi=3.14159 and sin=fn(x:real)=(.)and cos=fn(x:real)=(.)and tan=fn(x:real)=sin(x)/con(x)非法 并行声明仅当声明语句结束,各并行子声明同时生效,递归声明 用声明定义自己的声明,D=D 直接递归。名字一样就是递归 D1=D2 间接递归,或相互递归 D2=D1 指明递归(ML)val rec power=fn(x:real,n:int)=if

24、n=0 then 1.0 else if n 0 then 1.0/power(x,-n)else x*power(x,n-1)如果没有rec两次power应用出现则调其它地方定义的power而不是本身,声明作用域,嵌套块与可见性标识符和全名,作用起始 顺序 每一子声明结束即起作用 并行 所有子声明结束才起作用 递归 只要遇见相同被声明符,就起作用,可见性Visiability指简单标识符可引用该对象。嵌套有复盖名字冲突,同样名字出现在嵌套块中,按最近声明解释。被覆盖的只能加块名前缀(称全名),如outer.x才可使用。C+用:作用域分辨符。,正因为有冲突所以内部束定时只能束定全名,A.x,B

25、.y,块声明,把一个语句扩大到块,封闭的块有局部声明 Begin D;C end;D是声明集,C是语句集。说明作用域只限于此块 块声明:含有局部声明的声明,子声明的束定用于声明。ML:local fun multiple(n:int,d:int)=(n mod d=0)/函数说明 in fun leap(y:int)=(multiple(y,4)/用于另一函数定义 andalso not multiple(y,1000)orelse multiple(y,400)end,3.3.5 束定作用域与释义,束定与环境 在束定集的作用范围内称环境(environment)。词法作用域 程序正文给出的嵌

26、套声明的作用域称词法作用域,是词法子 集和覆盖父辈 PROGRAM A;VAR x,y:Integer;FUNCTION B(d:Integer):Integer;BEGIN b:=x+d END;FUNCTION C(d:Integer):Real;VAR x:Real;BEGIN x:=5.1;C:=x+B(d+1)END;BEGIN x:=3;y:=0 Writeln(C(y)END.当(x=3,y=0)时 C(0)=9.1,动态作用域和递归,递归每展开一层束定一次,且保留至递归终止。运行中动 态束定。因声明作用域大小不同。到底多大静态不得而知。词法作用域和动态作用域求值差异我们沿用上例

27、以LISP求C(0):(defun A(x,y)(printc(C y)/以最近束定释义(defun B(d)(+d x)(defun C(d)(let(x5.1)(+x(B(+d 1)(A 3 0)=(printc(C 0)/x=3,y=0=(printc(+5.1(B(+0 1)/x=5.1,d=0=(printc(+5.1(B(1)=(printc(+5.1(+1 5.1)/d=1,x就近取值=5.1=printc(+5.1 6.1)=(printc 11.2)/上例是9.1,作用域与生命期匹配 形参与实参结合(束定)实参(程序对象)寿命比该函数/过程长 静态(static)对象声明其寿

28、命比所在块长。指针实现的记录或结构的树或链其第一头指针在堆中分配,寿 命比堆栈帧内束定的变量长。文件变量的寿命一般比程序中对象长。束定机制与语言翻译器 类型语言编译高效,符号表不存在。局部束定要保留 无类型语言解释方便,符号表存在。词法作用域高效,排错易,但要事先知道如何束定 动态作用域灵活,方便,低效。有递归不可少。,3.4 计算组织-程序控制,冯诺依曼机器模型变量的时空特性对程序中求值的次序是十分敏感的表达式的求值次序是最低层的程序控制,在它的上层是四类控制:顺序控制、选择控制、重复(迭代)、函数或过程调用再上一层是对程序模块的控制。包括一个程序的各模块组织以及它们与环境的相互关系并发控制

29、也是一类控制,它可以在语句级,特征块和模块级实施并发控制,,,3.4.1 一般概述,语句级控制由于GOTO危害导致结构化程序。1966年Boehm和Jacopini回答了这个问题:任何流程图的计算逻辑都可以用顺序组、条件选择组、迭代组三种程序结构实现。保留GOTO的积极作用限制GOTO的副效应,把它们改头换面变为比较安全的顺序控制器(sequencer)。,命令式语言只要有赋值语句V=EXP,简单的逻辑条件IF(e)和GOTO语句就可以编出一切计算程序(输入/出除外)。,顺序控制,S1;S2 进一步扩展可为:S1;S2;;SnAda语句较全:简单_语句:=空_语句|赋值_语句|过程调用_语句|

30、goto_语句|入口调用_语句|出口_语句|返回_语句|引发_语句|夭折_语句|延迟_语句|代码_语句 其中空_语句,赋值_语句,延迟语句,代码_语句不影响控制和转移,exit(出口)语句,raise(引发)语句,abort(夭折)语句,return(返回)语句都是顺序控制器。,条件选择控制,if(e)无结构。Algol60改为ifthenelse结构,退化是ifthen 悬挂else if E1 then if E2 then S1 else S2 E1为真假均可执行S2 解决if E1 then begin if E2 then S1 else S2 end Pascal,Algol,C

31、if E1 then begin if E2 then S1 end else S2 if E1 then if E2 then S1 endif else S2 endif Fortran-77,Ada if E1 then if E2 then S1 else S2 endif endif 就近匹配,嵌套if和case e是同一表达式仅值不同 可改换ease Ada 的case语句 IF exp1 THEN ST1 ELSEIF exp2 THEN ST2 ELSEIF exp.ELSE SF3 ENDIF case Exp is switch(exp)when v1=S1;case v1

32、:S1;when v2=S2;break;.case v2:S2;when vm|vn=Sn;break;when others=St;default:St;end case;next;执行一个Si跳到end case 没有break要顺序执行,直到next,e1,e2,e3,S1,S2,S3,C以条件表达式实现选择控制 a=bc?b:c if(bc)a=b;else a=c;条件表达式 条件语句 命令式语言大量依赖顺序命令,纯函数式语言完全依赖写嵌套函数调用。,迭代控制,迭代一般用于处理同构的、聚集数据结构的重复操作,诸如数组、表、文件。显式迭代(循环),显示迭代有以下几种:,无限循环,有限

33、循环的三种形式:while_do,do_until,do_while_do,FORTRAN 初始 上界 增量DO L I=EXP1,EXP2,EXP3Adafor BACKDAY in reverse DAY RANGE loop 在循环控制变量和表达式约定上几十年来一直在演变 C采用迭代元素(Iteration element)iterate()=iterate(;)对迭代元素不加任何限制。语句,表达式,连续赋值,空均可。先算,再算,若不为零,则算然后执行。若为零出口。若不为零再算,若不为零重复以上步骤,若为零则出循环。,计数循环,例:用for循环计算表中元素之和,设表list是一简单结构,

34、一个成分是值value,另一个成分是指向下一表元素的指针next。可以有以下六种写法:1 先赋sum初值进入正规for 循环:sum=0;sum+=current-value;for(current=list;current!=NULL;current=current-next)sum+=current-value/循环控制变量current就用表list 2 sum在循环内赋初值:for(current=list,sum=0;current!=NULL;current=current-next)sum+=current-value 3 循环控制变量可以在for以外赋初值,增量在体内赋值:cu

35、rrent=list,sum=0;for(;current!=NULL;)sum+=current-value,current=current-next;/用,号连接的两语句成为一个命令表达式,4 循环条件为空,用条件表达式完成:current=list,sum=0;for(;)if(current=NULL)break;else sum+=current-value,current-next;/不提倡此种风格5 完全不用循环体,全部在循环条件中完成 for(current=list,sum=0;(current=current-next)!=NULL;sum+=current-value);

36、/效率最高.但list不能为NULL表 6 逻辑混乱,也能正确计算:current=list,sum=0;for(;current=current-next;sum+=current-value)if(current=NULL)break;/最坏的风格,list也不能为NULL表,3.4.2 异常处理,程序无法执行下去,也就是出现了异常(exception)情况。在早期语言的程序中,出现了这种情况就中断程序的执行,交由操作系统的运行程序处理。现在向用户开放,Ada,C+,Java,定义用户定义:exception;系统定义的异常(Ada):CONSTRAINT_ERROR 凡取值超出指定范围叫

37、约束异常 NUMBER_ERROR 数值运算结果值实现已不能表达 叫数值异常 STORAGE_ERROR 动态存储分配不够时,叫存储异常,异常定义与处理段,TASKING_ERROR 任务通信期间发生的异常叫任务异常。PROGRAM_ERROR 除上四种而外程序中发生的一切异常,都叫程序异常.如子程序未确立就调用等。预定义的可以显式也可以隐式引发,而用户定义的异常必须 显式引发 引发语句:raise;引发语句可出现在任何可执行语句所在的地方。一旦引发,则本程序块挂起转而执行本块异常处理段中同名的异常处理程序。,每个程序块都可以在该块末尾设立异常处理段:declare:;begin:;excep

38、tion:when=-异常处理段 when。-一般再次引发原异常(缺省名字)end;,Java的异常处理语句,try_catch_finally:try/可能有异常语句组/用throw 显式抛出异常/也可以隐式自动抛出异常 catch()/异常处理段-1/catch(捕捉)try 中抛出的异常对象/不匹配则跳过本catch 不执行 catch()/异常对象段-2/匹配执行后跳过所有catch finally/善后处理段/只要进入try块,本段必须执行/C+无此机制,其余同,Ada的嵌套异常及异常传播的 with TEXT_IO,MATH_FUNCTIONS;use TEXT_IO,MATH_F

39、UNCTIONS;procedure ONE_CIRCLE(DIAM:Float)is RADIUS,CIRCUMFERENCE,AREA:Float;begin CIRCUMFERENCE:=DIAM*22.0/7.0;RADIUS:=DIAM/2.0;AREA:=RADIUS*RADIUS*22.0/7.0;PRINT_CIRCLE(DIAM,RADIUS,CIRCUMFERENCE,AREA);7 exception when NUMBER_ERROR=raise;end ONE_CIRCLE;,procedure CIRCLES is DIAMETER:Float;MAXIMUM:co

40、nstant Float:=1.0e6;0TOO_BIG_ERROR:exception;PUT(please enter an float diameter.);begin 1loop begin GET(DIAMETER);if DIAMETER MAXIMUM then 2 raise TOO_BIG_ERROR;else 3 exit when DIAMETER PUT(Diameter too large!please reenter.);,5end;6end loop;PUT(“processing finished.”);PUT New_line;end CIRCLE;,3.5

41、函数和过程,命令式语言中子程序有两种形式:函数(必须返回值)也叫函数,过程(实施一组动作)也叫子例程subroutine。它们是程序的第一次分割,这种分割的好处:实施的功能单一,便于调试;相对独立,便于多人分工完成,且时间不受约束;相对封闭,人们易于控制,是分解复杂性的有力措施。子程序和主程序联系的接口特别重要。在这个界面上要指出该例程的数据特征,即输入什么输出什么。而整个子程序体是完成从输入到输出的实现手段。界面指出“做什么?”,而子程序体回答“怎么做”。80年代程序完成第二次分割:将子程序定义(即界面)和子程序体显式的分开,成为相对独立的规格说明(Specification)和体(body

42、)。,3.5.1 函数和过程抽象,函数抽象是用一个简单的名字抽象代表一个函数。函数由函数型构(Signature)和函数体(body)组成。函数计算的目的是求值。函数体等同于一个复合的表达式。函数抽象是对表达式的抽象过程抽象是用一个简单的名字抽象代表一个计算过程。过程由过程型构和过程体组成。过程调用的目的是执行一组命令过程抽象是对命令(即语句)集的抽象函数由函数型构和函数体组成。形式是:,function FUNC(fp1,fp2,.):returntype;/函数型构 B;/函数体,可包括任何声明和语句 其中fp1,fp2,为形式参数,也叫形式变元(argument)returntype 为

43、函数返回值的类型 函数引用是应用函数的唯一手段,它在同名的函数名之下给出实 在参数(实在变元):FUNC(ap1,ap2,.);,各种语言函数定义(a)FORTRAN INTEGER FUNCTION FACT(N)/前缀指明返回类型 INTEGER N,I,F/参数类型在此声明 F=1 DO 10 I=2,N F=F*1 10 CONTINUE FACT=F/必须至少定义函数名一次 RETURN/至少有一返回语句 END(b)Pascal FUNCTION fact(n:Integer):Integer;/参数类型在变元表中定义,BEGIN/后缀指明返回类型 fact=1;IF n=1 OR

44、 n=0 THEN Return ELSE Fact=n*fact(n-1);/也要定义函数名 END,(c)C int fact(n)/前缀指明返回类型 int n;/参数在体中声名类型 int i,f;/ANSI C改参数原型 f=1;if(n1)for(i=2;i/用函数定义值 if n=1 then 1 else n*fact(n-1),续,多重入口和指定返回,FORTRAN 的多重入口示例 SUBROUTINE DEG(R,THETA,X,Y)C=3.14159/180.0 THETA=C*THETA ENTRY RAD(R,THETA,X,Y)X=R*COS(THETA)Y=R*S

45、IN(THETA)RETURN END若THETA是度数值时,则调用语句为:CALL DEG(R,THETA,X,Y)/入口在子程序顶部若THETA是弧度值时,则:CALL RAD(R,THERA,X,Y)/入口在子程序中,FORTRAN的指定返回SUBROUTINE RM(X,Y,*,*,*).RETURN2/返回语句标号80.RETURN1/返回语句标号70.RETURN3/返回语句标号120.END CALL RM(A,B,70,80,120),形实参数表中元素个数,次序,类型应一致。早期语言都严格遵此准则。近代语言提供了较多的灵活表示法。Ada引入缺省参数,实参个数可少于形参个数;指明

46、参数结合不考虑次序;Ada引入参数模式in,out,inout指明只读,只写,读写参数。C语言允许任意多个参数的调用。如内定义函数printf()调用时可以写任意个输出,只是第一参数中的格式个数与参数个数对应。过程定义与调用 过程子程序定义形式 procedure PROC(fp1,fp2,.)/过程型构 B;/子程序体包含局声明 对应的过程调用是:PROC(ap1,ap2,.);C语言一切过程,包括主程序都是函数过程。它以void(无值)关键字代替函数类型指明符,实施子程序过程语义,引用或调用的形式,无参过程,-函数和过程的参数表均可为空。有的语言保留(),有的只有一个名字。一般无参过程也要

47、更新过程内部的值。函数过程还会返回不同的值。全局量在函数中有效。改变了全局量两次调用结果值当然不一样。这就是函数的副作用(side effect)。-有副作用的函数 C 在很大程度上利用函数副作用,例如,当需要跳过空白时写:while(c=getch()=);-Ada中常用的随机数:function RANDOM return FLOAT range 0.01.0;引用时,若FIELD已声明为常量:RESULT=RANDOM*FIELD;RANDOM若无副作用RESULT值不可能改变。,3.5.2 参数机制,语言中第一类对象均可作函数/过程参数。由于变量的时空特性,传递的形-实参数可以有许多不

48、同的实现结合的办法,即参数机制。,传值调用(call-by-value),1实参表达式先求值2将值复制给对应的形参。形参和实参有同样大小的存储3过程运行后一般不再将改变了的形参值复制回实参,Pascal中的传值调用 PROCEDURE test1(J2,A2:Integer;P2:list)BEGINWriteln(J2,A2,P2.value);J2=J2+1;P2=P2.next;Writeln(J2,A2,P2.value)END.调用程序有:test1(J1,A1J1,P1.next);,第一次打印为:1 30%第二次打印为:2 30$,传名调用(call-by-name),传名在过程

49、/函数中加工的就是实参已分配的值,因此不需付出双倍存储代价。但传名过程的虚实结合是将程序体中所有形参出现的地方均以实参变元名置换。这样出现几次算几次效率是低的。传名调用程序示例由于Pascal无传名机制,此处作一点扩充:PROCEDURE test2(NAME J2,A2:Integer;NAME P2:List);函数体同test1执行同样调用:test2(J1,A1J1,P1.next);,名结合后打印:1 30%执行后结果是:2 45$,引用调用(call_by_reference),引用参数实现时,编译器在子程序堆栈中设许多中间指针,将形参名束定于此指针,而该指针的值是实参变量的地址(

50、在主程序堆栈框架内),在子程序中每次对形参变量的存取都是自动地递引用到实参存储对象上。引用调用的Pascal示例:PROCEDURE test3(VAR J2,A2:Integer;VAR P2:List);函数体同test1相应的调用程序是:test3(J1,A1J1,P1.next);,第一次打印是:1 30%第二次打印是:2 30$,引用调用图示,参数模式与返回调用(call-by-return),显式指明参数传递模式,可以为编译实现提供信息 fun_name(x,y:Real;VAR s,q:Integer).x,y传值实现,它只读。s,q引用实现,可读/写Ada只规定参数模式in,o

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号