本章要点:
过程与函数的定义
过程与函数的参数
常用的过程与函数
过程与函数的嵌套与递归第 5章 过程与函数
5.1 基本概念过程与函数是程序设计语言中的重要概念,也是实现结构化程序设计思想的重要手段。结构化程序设计思想的要点之一就是对一个复杂问题采用
“分而治之”的策略 —— 模块化。把一个较大的程序划分成若干个模块,
每个模块只完成一个或若干个功能。每个功能可以用一个程序段来实现,
这个程序段称为子程序。根据子程序是否有返回值可分为过程与函数两种。
函数是由变量声明部分与可执行语句组成的独立实体,用以完成一定的功能。它的主要特点是在函数执行后可以方便地返回所需值,在某些情况下使用比较简单。
5.1.1 函数的定义与使用函数也是一段相对独立的程序段,可读取参数、执行一系列语句并改变其参数的值。
定义函数的格式为:
5.1.1 函数的定义与使用 (1)
Function <函数名 > [ ( <形参表 > ) ],< 返回类型 >;
[<局部声明 > ]
begin
[ < 语句列 > ] ;
end;
以保留字 Function开头的第一行,称为函数首部,其余部分称为函数体。
1,函数首部函数名使用与变量名相同的命名规则,一个函数只能有一个函数名。
形参表由若干个形式参数组成,它指明了可以传递给函数参数的个数和类型。所有的形式参数必须说明类型,相同类型的形参之间用逗号隔开,
不同类型的形参之间用分号隔开。函数必须规定函数返回值的类型,函数返回值通过函数名或 Result(隐含变量)带回。
5.1.1 函数的定义与使用 (2)
2,函数体函数体是一个程序段。由局部声明部分和可执行部分组成。可执行部分由
begin 开始,以 end结束,end 后面必须跟一个分号。在局部声明部分定义函数所需的常量、变量、类型等,该部分声明的常量、变量、类型只能在函数内部使用。在可执行部分根据函数的功能编程,而且在执行语句中至少要给函数名预定义的 Result赋值一次,以便在函数执行结束后函数值带回到主程序中去。
3,函数的调用函数的调用比较简单,调用格式为:
函数名 ( <实参表 >);
更常用的方法是直接将函数返回值直接赋值给变量,当然要注意类型兼容。
例如声明一个 Max函数:
function Max(a:real;b:string):boolean;
则在调用 Max函数时必须有两个参数,而且参数类型必须相兼容,返回值赋值也要注意类型兼容。
5.1.1 函数的定义与使用 (3)
2,函数体函数体是一个程序段。由局部声明部分和可执行部分组成。可执行部分由
begin 开始,以 end结束,end 后面必须跟一个分号。在局部声明部分定义函数所需的常量、变量、类型等,该部分声明的常量、变量、类型只能在函数内部使用。在可执行部分根据函数的功能编程,而且在执行语句中至少要给函数名预定义的 Result赋值一次,以便在函数执行结束后函数值带回到主程序中去。
3,函数的调用函数的调用比较简单,调用格式为:
函数名 ( <实参表 >);
更常用的方法是直接将函数返回值直接赋值给变量,当然要注意类型兼容。
例如声明一个 Max函数:
function Max(a:real;b:string):boolean;
则在调用 Max函数时必须有两个参数,而且参数类型必须相兼容,返回值赋值也要注意类型兼容。
5.1.1 函数的定义与使用 (4)
【 例 5-1】 定义一个求两个实数中较大数的函数,并利用该函数求三个实数中的最大值 。
分析:定义一个新的函数,函数名为M ax(),该函数的功能是对两个实数求出最大值,输入的参数是两个实数 x,y,注意输入量不能为其他类型的常量或变量,输出 z是两个实数中较大的一个值,在程序中可以利用 IF语句实现两数的比较。
求三个数中的最大值可以先求两个数中的最大值,再与第三个数做比较,
大者为三个数中的最大值。
启动 Delphi 7.0,新建一个工程,在其中添加标签 Label,Label2,将两标签的 Caption属性值分别改为“输入三个实数”和“三个数中的最大值是”,
并适当更改字体属性。添加四个编辑框,默认名分别为 Eidt1,Eidt2,Edit3、
Edit4,将它们的 Text属性值均改为,0”,再添加一个按钮 Button1并将其
Caption属性值改为“比较”界面,将组件按适当位置摆放,如图 5-1所示。
5.1.1 函数的定义与使用 (5)
双击“比较”按钮,在自动生成的 TForm1.Button1Click()过程中添加变量定义语句和事件处理语句。
procedure TForm1.Button1Click(Sender,TObject);
var a,b,c:real;
begin
a:=strtofloat(edit1.text);
b:=strtofloat(edit2.text);
c:=strtofloat(edit3.text);
edit4.text:=floattostr(max(max(a,b),c));
end; 图 5-1 比较三个数的大小
双击“比较”按钮,在自动生成的 TForm1.Button1Click()过程中添加变量定义语句和事件处理语句。
procedure TForm1.Button1Click(Sender,TObject);
var a,b,c:real;
begin
a:=strtofloat(edit1.text);
b:=strtofloat(edit2.text);
c:=strtofloat(edit3.text);
edit4.text:=floattostr(max(max(a,b),c));
end;
5.1.1 函数的定义与使用 (6)
在这个求三个数最大值过程中用到了两个标准函数 strtofloat()和 floattostr()、
一个用户自定义函数 max()。其中 strtofloat()函数的作用是把字符串转化为实数,而 floattostr()函数的作用与之相反,它的作用是把实数转化为字符串。
由于 Object Pascal是一种强类型语言,所以在编程中应当注意类型的转化。
为了使 Max()函数能够被正常调用,必须将M ax()的定义语句添加到按钮事件声明语句之前。该函数定义如下:
function max(x,y:real):real;
//该句也可以写为 function max(x:real;y:real):real;
begin
5.1.1 函数的定义与使用 (7)
if x>y then result:=x
else result:=y;
end;
保存工程并运行,在三个编辑框中分别输入三个实数,单击
“比较”按钮则在第四个编辑框中显示出三个数中的最大值。
注意不得输入非法字符,否则程序会出现运行错误。
5.1.2 过程的定义与使用 (1)
Object Pascal的过程分标准过程和自定义过程两种。标准过程是指系统内部定义的过程,无需编写代码可直接调用。而自定义过程则是由程序员编写代码用以完成指定的操作,包括事件过程和通用过程。
过程在使用之前必须定义,定义一个过程也称为过程声明。定义一个过程的一般形式为:
Procedure < 过程名 > [ ( < 形参表 > ) ]
[ < 局部声明 > ]
begin
[ < 语句列 > ]
end;
1,过程首部过程名的命名规则与变量名相同,一个过程只能有一个惟一的过程名。
形参表由若干个形式参数组成,它指明了从调用过程传递给过程的实际参数个数和类型,所有的形式参数必须说明类型,相同类型的形参之间用逗号隔开,不同类型的形参之间用分号隔开。
5.1.2 过程的定义与使用 (2)
2,过程体过程体是一个程序段,由局部声明和执行语句组成。局部声明部分用来声明该过程中所使用的类型、常量、变量等,这些只限于过程内部使用,与过程外的同名对象无关。可执行部分由 begin 开始,以
end结束,end 后面必须跟一个分号。
3,过程的调用过程的调用格式为,
过程名(参数表);
过程声明必须在过程调用语句之前,调用过程时也要注意参数类型兼容。
在编程过程中,程序员要对各种事件进行相关的代码处理,创建相应的事件处理过程。
在对象观察器( Object Inspector)中选择指定对象,然后在事件
( Events)选项卡中选择指定的事件名称,并用鼠标双击其右边的下拉列表框,Object Pascal将自动产生一个事件过程。事件过程的默认名称是组件名加上事件类型名。
5.1.2 过程的定义与使用 (3)
例如,新建一个工程,添加一个按钮组件,命名为 Button1,现在要添加按钮组件的单击事件处理过程,在对象观察器的下拉列表中选择 Button1,在事件选项卡中选择 OnClick事件并单击右边的下拉列表框,Object Pascal自动生成代码,Button1的单击事件默认事件过程是 Button1Click(),代码如下:
procedure TForm1.Button1Click(Sender,TObject);
begin
end;
程序员只需在 begin与 end之间加入相应的事件处理代码即可。
该过程的参数是一个O bject类型的对象。
当用户对某个对象发出一个动作时,Windows会通知 Object Pascal产生了一个事件,Object Pascal将自动调用与该对象事件相关的事件过程。即对象和代码之间建立了联系,所以说事件过程是依附于对象的。
另外,一个事件过程也可以被多个对象、多个事件共享。其方法是:首先为其中的一个对象建立事件过程,然后在创建其他对象事件过程时,从事件名称右边的下拉列表中选择已建立的事件即可。
5.2 常用过程与函数数学运算函数用于各种数学运算。常用的数学运算函数有如下几种。
1,绝对值函数
function Abs(X);
返回参数 X 的绝对值。参数 X 可以是整型或实型,返回值为非负的整数实数。
2,平方与平方根函数
function Sqr(X,Extended),Extended;
参数 X 为实型表达式,返回 X 的平方,返回值也是实型数据。
function Sqrt(X,Extended),Extended;
参数 X 为非负实型表达式,返回 X 的算术平方根,返回值也是实型数据。
为了尽可能地减少程序员编写应用程序的工作量,Object Pascal提供了大量的过程与函数 。 下面分类介绍一些常用的过程与函数 。
5.2.1 数学运算函数
5.2.1 数学运算函数
3,三角函数
function Sin(X,Extended),Extended;
function Cos(X,Extended),Extended;
function ArcTan(X,Extended),Extended;
4,取整数部分与取小数部分函数
function Int(X,Extended),Extended;
function Frac(X,Extended),Extended;
5,取整函数
function Trunc(X,Extended),Int64;
function Round(X,Extended),Int64;
6,指数函数和对数函数
function Exp(X,Real):Real;
function Ln(x:Real):Real;
7,随机函数
function Random [ ( Range,Integer) ];
8,π函数
Object Pascal提供了一个返回圆周率的函数,该函数定义为:
function Pi:Extended;
5.2.2 字符处理函数 (1)
1.大小写转换函数
function LowerCase(const S,string),string;
function UpperCase(const S,string),string;
2.合并字符串函数
procedure AppendStr(var Dest,string; const S,string);
function Concat(S1 [,S2,...,Sn],string),string;
3,查找字符串函数
function Pos(Substr,string; S,string),Integer;
4,求字符串长度函数
function Length(S),Integer;
5,数值与字符串转换函数
procedure Str(X [,Width [,Decimals ]]; var S);
procedure Val(S; var V; var Code,Integer);
function IntToStr(Value,Integer),string;
function FloatToStr(Value,Extended),string;
function StrToInt(const S,string),Integer;
function StrToFloat(const S,string),Extended;
5.2.2 字符处理函数 (2)
6,格式化函数
function Format(const Format,string; const Args,array of const),string;
用来将字符串按指定的格式返回。其中第一个参数 format 是“格式字符串”,可以包含字符信息,也可以包含数组 Args中参数的类型和格式信息。第二个参数是一个数组,从左往右每个元素与第一个参数中的类型格式信息一一对应。
类型和格式信息由字符,%”开头,格式如下:
%[<参数位置 >:][-][<所占宽度 >][.<小数位数 >]<类型 >
若缺省参数位置,则从左到右依次分配各参数。,%”之后跟参数位置则必须跟“:”,参数位置指明了当前格式用于第几个数组参数。
5.2.3 对话框函数与对话框过程 (1)
对于一些简单的输入和输出,Windows程序最常用的方法是使用对话框,对话框是用户与应用程序交换信息最方便的途径之一 。 Delphi
7.0提供了多个对话框标准函数供程序员使用,常用的有以下几个函数与过程 。
图 4-1 静态数组
1,ShowMessage过程
Delphi 7.0提供了一个用于显示消息的标准过程,定义如下:
procedure ShowMessage(const Msg,string);
ShowMessage过程的参数是一个字符串常量。 ShowMessage过程的作用是显示一个最简单的对话框,对话框以应用程序的执行文件名作为标题,对话框右上侧是一个关闭按钮,在对话框中显示了字符串常量,也就是用户要显示的信息内容,可以使用硬回车符(# 13)使文本换行。在对话框下部是一个 OK按钮,单击按钮关闭对话框返回应用程序。
2,ShowMessageFmt过程
Delphi 7.0提供了另一个用于显示消息的标准过程,定义如下:
procedure ShowMessageFmt(const Msg,string; Params,array of const);
ShowMessageFmt过程的参数是一个格式字符串和一个数组,与 Format函数相似,可以将用户显示的信息按一定格式显示在对话框中。
5.2.3 对话框函数与对话框过程 (2)
【 例 5-2】 设计程序:利用对话框实现输入球的半径,输出球的体积与表面积。
分析:利用编辑框输入球的半径,计算球的体积可利用数学公式 V=4πr3/3,计算球的表面积可利用数学公式 S=4πr2,其中 π的值可以通过 Pi函数取得。
启动 Delphi 7.0,新建一个工程,在窗体中添加一个标签 Label1,一个编辑框 Edit1和一个按钮 Button1,将 Label1的 Caption属性值改为“输入球的半径:”,将 Edit1的 Text属性值改为,1”,将 Button1的 Caption属性值改为“计算”。如图 5-3所示。
5-3 计算球的体积和表面积
5.2.3 对话框函数与对话框过程 (3)
图 4-1 静态数组双击“计算”按钮,在事件处理过程中添加变量说明和过程代码:
procedure TForm1.Button1Click(Sender,TObject);
var
r,V,S:real;
begin
r:=strtofloat(edit1.Text );
V:=4/3*Pi*r*r*r;
S:=4*Pi*r*r;
ShowMessageFmt(‘球的体积是,%12.4f’+#13+‘球的表面积 是,%12.4f',[V,S]);
end;
保存工程并运行,当单击“计算”按钮时,弹出如图 5-3所示的对话框,
单击 OK按钮后对话框关闭。可以输入任意实数,计算球的体积和表面积。
5.2.3 对话框函数与对话框过程 (4)
3,InputBox函数
Delphi 7.0提供了一个用于输入信息的标准函数,定义如下:
function InputBox(const ACaption,APrompt,ADefault,string),string;
该函数显示一个能接受用户输入的对话框,并以字符串的形式返回用户输入的信息。
InputBox函数有三个参数,第一个参数 Acaption是对话框的标题,第二个参数
Aprompt是提示信息的内容,第三个参数 Adefault指定了默认的输入内容。
将 【 例 5-2】 中的 r:=strtofloat(edit1.Text )语句改为如下语句:
r:=strtofloat(inputbox('输入半径 ','input:','1'));
保存并运行工程,当单击“计算”按钮时弹出如图 5-4所示对话框。
其中对话框的标题为“输入半径”,提示内容为,input:”,输入框中的默认输入内容为“1”,可以对输入内容进行更改。在对话框中有两个按钮,若用户单击 OK按钮则输入内容为函数返回值,若用户单击 Cancel按钮则默认值为函数返回值。
图 5-4 对话框
5.2.3 对话框函数与对话框过程 (5)
4,MessageDlg函数
Object Pascal提供了一个用于显示信息对话框的标准函数,定义如下:
function MessageDlg(const Msg,string; DlgType,TMsgDlgType; Buttons,
TMsgDlgButtons; HelpCtx,Longint),Word;
该函数可以显示一个对话框提示用户,返回值为 Word类型的数据。
MessageDlg函数的参数有四个,第一个参数 Msg用于在对话框中显示相关提示信息,第二个参数 DlgType用于指定对话框的类型,取值及含义如表 5-
2所示。
各个参数的取值及含义见本教程 P68页
5.3 子程序的参数当程序代码在调用一个过程或函数时,通常用参数传递数据到被调用的过程或函数中 。 形式参数是指在定义过程或函数时,出现在过程或函数参数表中的变量名,表示用于接收数据的变量,也简称形参 。 实际参数是指在调用过程或函数时,传送给过程或函数的常量,变量或表达式,也简称实参 。
调用过程与函数的目的,就是要完成某一工作。调用者要把条件告诉过程与函数,过程与函数要把结果传回调用者。通常在编制一个子程序时,要考虑它需要哪些输入量,进行处理和运算后要输出哪些输出量。正确地提供输入输出是子程序使用的关键问题。
5.3.1 形式参数与实际参数对实参有以下几点说明:
( 1)实参列表与形参列表中对应的变量名不必相同,但实参个数、类型必须与形参相一致,否则会发生类型不匹配错误;
( 2)在调用过程或函数时,实参的书写顺序必须与过程或函数定义中相应形参的顺序相同,多个实参则各参数间用逗号隔开,调用时,第一个形参接收第一个实参的值,第二个形参接收第二个实参的值,… 。
5.3.2 参数传递方式(1)
按照参数传递方式的不同,Object Pascal中过程与函数的参数可以分为变量参数,数值参数,常量参数和外部参数四种 。
1,变量参数在过程或函数首部形参列表中的参数前面如果使用保留字 var进行说明,则表示该参数为变量参数。
变量参数传递方式为“按地址传递”,即将实参的地址传递给形参,这样形参与实参表示同一个储存单元,在过程或函数的运算过程中如果改变了形参(也就是实参)的值,这个值的改变将影响到过程或函数调用之后。
假设有以下一个函数的声明:
function DoubleByRef(var X,Integer),Integer; //X是一个变量参数
begin
X,= X * 2;
Result,= X;
end;
该函数定义中声明了一个变量参数 X,在函数运算中将 X的值扩大了一倍,
改变了参数的值。
5.3.2 参数传递方式(2)
在调用该函数后实参的值改变如下:
Var a,b,c,Integer;
begin
a:= 4;
b:=DoubleByRef(a); //执行后 b = 8,a = 8
//调用函数后值的改变将影响到函数调用之后。此时 a的值为 8
c:=DoubleByRef(a); //执行后 c = 16,a = 16
end;
在编程中应注意观察过程或函数对变量参数的操作,也可以用变量参数将处理结果返回。
【 例 5-3】 编写一个交换两个实数的过程。
分析:过程的功能是实现两个实数的交换。过程的输入为两个实数,要将交换后的结果返回可以利用变量参数实现。
代码如下:
procedure swap(var a,b,real);
var c:real;
begin
c:=a;a:=b;b:=a;end;
5.3.2 参数传递方式(3)
2,数值参数在过程或函数首部形参列表中的参数前面如果没有保留字进行说明,则表示该参数为数值参数。
数值参数的传递方式为“按值传递”,即将实参的值传递给形参,如果在过程或函数中改变了形参的值,不会影响实参变量。相当于在调用过程或函数时,重新开辟一个存储单元,将实参值赋予该存储单元,并对该单元进行运算和处理,过程或函数调用结束后将存储单元释放。
3,常量参数在过程或函数首部形参列表中的参数前面如果使用保留字 const进行说明,则表示该参数为常量参数。
对常量参数而言,不论实参是常量或变量,在过程或函数的调用中数值总是不能被改变。常量参数就像局部常量或只读变量,在过程或函数主体中不能对常量参数赋值,也不能将其作为变量参数传递给其他过程或函数。
使用 const为变量提供了安全保障,确保实参的值不被改变。
5.3.2 参数传递方式(4)
4,外部参数在过程或函数首部形参列表中的参数前面如果使用保留字 out进行说明,
则表示该参数为外部参数。
外部参数与变量参数一样,其传递方式为“按地址传递”,但外部参数与变量参数使用时有很大区别。对于 out参数,变量的初始值被过程或函数丢弃,out参数仅用于输出,也就是说,它告诉函数或过程在哪里存储输出,而不提供任何输入。
out参数经常用于分布式对象模块,如 COM和 CORBA。此外,向函数或过程传递未初始化的变量时,也可以考虑使用 out参数。
5.3.3 使用缺省参数(1)
在过程或函数首部可以指定缺省参数的值。缺省值仅允许用于有类型的常量参数和数值参数。要提供缺省值,需要在参数声明的末尾使用等号
( =)并跟随一个与参数类型赋值兼容的常量表达式。
例如,给出如下声明:
procedure SampleProcedure(x:real,y:real=10);
begin
…
end;
对上述过程,下面的两个语句是等效的:
SampleProcedure(a);
SampleProcedure(a,10);
在程序调用中指定值将覆盖在过程或函数定义中指定的缺省值。因此,对于
5.3.3 使用缺省参数(2)
如下声明:
SampleProcedure(a,20);
第二个参数传入的值是 20。
多个参数的声明不能指定一个缺省值。因此,下面的声明:
function MyFunction(X,Real = 3.5; Y,Real = 3.5),Real;
是合法的,而下面的声明:
function MyFunction(X,Y,Real = 3.5),Real; //语法错误是不合法的。
含有缺省值的参数必须出现参数列表的末尾。简而言之,跟随在第一个声明了缺省值的参数之后的所有参数都必须也有缺省值。因此,下面的声明是不合法的:
procedure MyProcedure(I,Integer = 1; S,string);
5.3.4 数组参数声明接受数组参数的过程时,不能在参数声明中包括类型说明。下面的声明将导致编译错误:
procedure Sort(A,array[1..10] of Integer); //语法错误而下面的声明则是有效的:
type TDigits = array[1..10] of Integer;
procedure Sort(A,TDigits);
在一个过程与函数中包含另外一个过程与函数,称为子程序的嵌套。
子程序不仅可以调用其他子程序,还可以调用自身,子程序直接调用自身称为直接递归,子程序间接调用自身称为间接递归。
在解决实际问题时经常利用过程与函数的嵌套与递归使复杂问题简单化。
5.4 子程序的嵌套与递归
5.4.1 子程序的嵌套在 Delphi 7.0中子程序有一定的层次结构,在一个子程序的内部可以再定义一个新的子程序,即子程序可以嵌套定义。
子程序的嵌套要求外层子程序能够完全包含内层子程序,不允许局部包含,
即不允许出现第一个子程序的定义还没有结束,又开始一个定义新的子程序。
子程序不但可以嵌套定义也可以嵌套调用,即在一个子程序中调用另一个子程序。
在 Object Pascal中,过程与函数必须先说明再调用,子程序的调用必须遵守以下规则:
( 1)子程序可以调用其内层的子程序但不能隔层调用,如图 5-5所示,P2
可以调用 P2A和 P2B,但不能调用 P2AB;
( 2)内层子程序可以调用外层的子程序,而且允许隔层调用。如 P1A可以调用 P1,P2B可以调用 P2;
( 3)同一层的子程序,允许后定义的子程序调用先定义的子程序,如 P1B
可以调用 P1A,P2可以调用 P1,但 P2A不能调用 P1A;
Uint1P1P1AP1BP2P2AP2BP2AB
( 4)如果需要调用同层中后定义的子程序,可以用保留字 forward(超前引用)进行前置说明,即在还未进行代码实现的过程与函数的声明部分最后加上保留字 forward。
5.4.2 子程序的递归(1)
递归的主要特点是子程序直接或间接地调用自身。递归在算法描述中有非常重要的作用,许多复杂的问题都可以通过递归得到解决,递归在处理阶乘运算、级数运算、幂指数运算等方面特别有效。
直接递归调用的例子如下:
function fun(n:integer):integer;
var a,b:integer;
begin
…
a:=fun(b);
…
end;
明显地,在编写程序中不应当出现无休止地递归调用,而只能出现有限次数的调用,这可以通过程序流程控制来实现。
【 例 5-4】 定义一个求自然数 n的阶乘的函数。
5.4.2 子程序的递归(2)
分析:计算一个自然数 n的阶乘可以采用直接相乘的方法,n乘以 ( n-1) 再乘以 ( n-2) …,一直乘到 1,这是比较容易理解的一种方法,可以利用循环语句方便地实现 。 还可以利用递归的方法,求自然数 n的阶乘也可写成如下数学定义:
求解 n的阶乘可以利用 (n-1)的阶乘来实现,如 5! =5*4!,而 4!
=4*3! …,直到 1! =1,即可得到最终结果。
实现代码如下:
function mult(n:integer):integer;
begin
if n=0 then
mult:=1
else mult:=n*mult(n-1);
end;
对于间接递归,最简单的一种情况是函数 A调用函数 B,而函数 B又调用函数 A的情况。此时函数要进行前置说明,即在还未进行代码实现的过程与函数的声明部分最后加上保留字 forward。(俱体分析见本教程 P74页)
0)!1(
01!
nnn
nn
5.5 变量的作用域变量的作用域是指变量能被某一子程序识别的范围 。 在一个工程的某一部分声明一个变量之后,这个被声明的变量是不是在程序中处处可用? 答案是否定的,每个变量都有它的作用范围 。
5.5.1 公有变量与私有变量在单元的接口部分声明的变量属于公有变量,该变量不但能被该单元中的过程与函数引用,也可以被其他单元引用。
一个单元需引用其他单元的变量时,只要在 uses语句中指明其他单元即可。
新建一个工程,观察 unit1中的 uses语句,
uses
Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,
Forms,Dialogs;
对于该单元可以引用 Windows,Messages,SysUtils等单元接口部分声明的变量。
对一个单元,在关键字 Interface(接口)之后关键字 Implementation(实现)之前声明的变量、类型或过程都是可以被其他单元引用的。
在关键字 Implementation(实现)之后声明的变量属于私有变量,它只被该单元访问,
不能被其他单元访问,相当于是该单元的私有部分。
5.5.2 全局变量与局部变量(1)
在所有子程序声明之前,在关键字 Implementation(实现)之后声明的变量属于全局变量,在关键字 Interface(接口)部分声明的变量也属于全局变量。
全局变量的作用域是整个单元,即在单元中声明的所有子程序中都是可见的和有定义的,可以对其操作或引用。而局部变量的作用域是单个子程序,在单个子程序中它是有定义的,在该子程序之外它不能被其他子程序引用或访问。
在一个子程序内部定义的变量是内部变量,它只在该子程序范围内有效,
也就是说只有在该子程序内部才能使用它们,在此子程序之外是不能使用这些变量的,这种变量称为局部变量 。
注意,Delphi 7.0中不能在程序中声明变量,只能在程序开始之前对变量进行声明
unit Unit1;
interface
implementation
{$R*.dfm}
function…
…
V1
V2
V3
5.5.2 全局变量与局部变量(2)
如图 5-6所示,在 V1部分声明的变量属于公有变量,V2,V3部分声明的变量属于私有变量;而 V1,V2部分声明的变量属于全局变量,V3部分声明的变量属于局部变量。公有私有是相对外部单元而言,而全局、局部是相对单元内部而言。
过程与函数的定义
过程与函数的参数
常用的过程与函数
过程与函数的嵌套与递归第 5章 过程与函数
5.1 基本概念过程与函数是程序设计语言中的重要概念,也是实现结构化程序设计思想的重要手段。结构化程序设计思想的要点之一就是对一个复杂问题采用
“分而治之”的策略 —— 模块化。把一个较大的程序划分成若干个模块,
每个模块只完成一个或若干个功能。每个功能可以用一个程序段来实现,
这个程序段称为子程序。根据子程序是否有返回值可分为过程与函数两种。
函数是由变量声明部分与可执行语句组成的独立实体,用以完成一定的功能。它的主要特点是在函数执行后可以方便地返回所需值,在某些情况下使用比较简单。
5.1.1 函数的定义与使用函数也是一段相对独立的程序段,可读取参数、执行一系列语句并改变其参数的值。
定义函数的格式为:
5.1.1 函数的定义与使用 (1)
Function <函数名 > [ ( <形参表 > ) ],< 返回类型 >;
[<局部声明 > ]
begin
[ < 语句列 > ] ;
end;
以保留字 Function开头的第一行,称为函数首部,其余部分称为函数体。
1,函数首部函数名使用与变量名相同的命名规则,一个函数只能有一个函数名。
形参表由若干个形式参数组成,它指明了可以传递给函数参数的个数和类型。所有的形式参数必须说明类型,相同类型的形参之间用逗号隔开,
不同类型的形参之间用分号隔开。函数必须规定函数返回值的类型,函数返回值通过函数名或 Result(隐含变量)带回。
5.1.1 函数的定义与使用 (2)
2,函数体函数体是一个程序段。由局部声明部分和可执行部分组成。可执行部分由
begin 开始,以 end结束,end 后面必须跟一个分号。在局部声明部分定义函数所需的常量、变量、类型等,该部分声明的常量、变量、类型只能在函数内部使用。在可执行部分根据函数的功能编程,而且在执行语句中至少要给函数名预定义的 Result赋值一次,以便在函数执行结束后函数值带回到主程序中去。
3,函数的调用函数的调用比较简单,调用格式为:
函数名 ( <实参表 >);
更常用的方法是直接将函数返回值直接赋值给变量,当然要注意类型兼容。
例如声明一个 Max函数:
function Max(a:real;b:string):boolean;
则在调用 Max函数时必须有两个参数,而且参数类型必须相兼容,返回值赋值也要注意类型兼容。
5.1.1 函数的定义与使用 (3)
2,函数体函数体是一个程序段。由局部声明部分和可执行部分组成。可执行部分由
begin 开始,以 end结束,end 后面必须跟一个分号。在局部声明部分定义函数所需的常量、变量、类型等,该部分声明的常量、变量、类型只能在函数内部使用。在可执行部分根据函数的功能编程,而且在执行语句中至少要给函数名预定义的 Result赋值一次,以便在函数执行结束后函数值带回到主程序中去。
3,函数的调用函数的调用比较简单,调用格式为:
函数名 ( <实参表 >);
更常用的方法是直接将函数返回值直接赋值给变量,当然要注意类型兼容。
例如声明一个 Max函数:
function Max(a:real;b:string):boolean;
则在调用 Max函数时必须有两个参数,而且参数类型必须相兼容,返回值赋值也要注意类型兼容。
5.1.1 函数的定义与使用 (4)
【 例 5-1】 定义一个求两个实数中较大数的函数,并利用该函数求三个实数中的最大值 。
分析:定义一个新的函数,函数名为M ax(),该函数的功能是对两个实数求出最大值,输入的参数是两个实数 x,y,注意输入量不能为其他类型的常量或变量,输出 z是两个实数中较大的一个值,在程序中可以利用 IF语句实现两数的比较。
求三个数中的最大值可以先求两个数中的最大值,再与第三个数做比较,
大者为三个数中的最大值。
启动 Delphi 7.0,新建一个工程,在其中添加标签 Label,Label2,将两标签的 Caption属性值分别改为“输入三个实数”和“三个数中的最大值是”,
并适当更改字体属性。添加四个编辑框,默认名分别为 Eidt1,Eidt2,Edit3、
Edit4,将它们的 Text属性值均改为,0”,再添加一个按钮 Button1并将其
Caption属性值改为“比较”界面,将组件按适当位置摆放,如图 5-1所示。
5.1.1 函数的定义与使用 (5)
双击“比较”按钮,在自动生成的 TForm1.Button1Click()过程中添加变量定义语句和事件处理语句。
procedure TForm1.Button1Click(Sender,TObject);
var a,b,c:real;
begin
a:=strtofloat(edit1.text);
b:=strtofloat(edit2.text);
c:=strtofloat(edit3.text);
edit4.text:=floattostr(max(max(a,b),c));
end; 图 5-1 比较三个数的大小
双击“比较”按钮,在自动生成的 TForm1.Button1Click()过程中添加变量定义语句和事件处理语句。
procedure TForm1.Button1Click(Sender,TObject);
var a,b,c:real;
begin
a:=strtofloat(edit1.text);
b:=strtofloat(edit2.text);
c:=strtofloat(edit3.text);
edit4.text:=floattostr(max(max(a,b),c));
end;
5.1.1 函数的定义与使用 (6)
在这个求三个数最大值过程中用到了两个标准函数 strtofloat()和 floattostr()、
一个用户自定义函数 max()。其中 strtofloat()函数的作用是把字符串转化为实数,而 floattostr()函数的作用与之相反,它的作用是把实数转化为字符串。
由于 Object Pascal是一种强类型语言,所以在编程中应当注意类型的转化。
为了使 Max()函数能够被正常调用,必须将M ax()的定义语句添加到按钮事件声明语句之前。该函数定义如下:
function max(x,y:real):real;
//该句也可以写为 function max(x:real;y:real):real;
begin
5.1.1 函数的定义与使用 (7)
if x>y then result:=x
else result:=y;
end;
保存工程并运行,在三个编辑框中分别输入三个实数,单击
“比较”按钮则在第四个编辑框中显示出三个数中的最大值。
注意不得输入非法字符,否则程序会出现运行错误。
5.1.2 过程的定义与使用 (1)
Object Pascal的过程分标准过程和自定义过程两种。标准过程是指系统内部定义的过程,无需编写代码可直接调用。而自定义过程则是由程序员编写代码用以完成指定的操作,包括事件过程和通用过程。
过程在使用之前必须定义,定义一个过程也称为过程声明。定义一个过程的一般形式为:
Procedure < 过程名 > [ ( < 形参表 > ) ]
[ < 局部声明 > ]
begin
[ < 语句列 > ]
end;
1,过程首部过程名的命名规则与变量名相同,一个过程只能有一个惟一的过程名。
形参表由若干个形式参数组成,它指明了从调用过程传递给过程的实际参数个数和类型,所有的形式参数必须说明类型,相同类型的形参之间用逗号隔开,不同类型的形参之间用分号隔开。
5.1.2 过程的定义与使用 (2)
2,过程体过程体是一个程序段,由局部声明和执行语句组成。局部声明部分用来声明该过程中所使用的类型、常量、变量等,这些只限于过程内部使用,与过程外的同名对象无关。可执行部分由 begin 开始,以
end结束,end 后面必须跟一个分号。
3,过程的调用过程的调用格式为,
过程名(参数表);
过程声明必须在过程调用语句之前,调用过程时也要注意参数类型兼容。
在编程过程中,程序员要对各种事件进行相关的代码处理,创建相应的事件处理过程。
在对象观察器( Object Inspector)中选择指定对象,然后在事件
( Events)选项卡中选择指定的事件名称,并用鼠标双击其右边的下拉列表框,Object Pascal将自动产生一个事件过程。事件过程的默认名称是组件名加上事件类型名。
5.1.2 过程的定义与使用 (3)
例如,新建一个工程,添加一个按钮组件,命名为 Button1,现在要添加按钮组件的单击事件处理过程,在对象观察器的下拉列表中选择 Button1,在事件选项卡中选择 OnClick事件并单击右边的下拉列表框,Object Pascal自动生成代码,Button1的单击事件默认事件过程是 Button1Click(),代码如下:
procedure TForm1.Button1Click(Sender,TObject);
begin
end;
程序员只需在 begin与 end之间加入相应的事件处理代码即可。
该过程的参数是一个O bject类型的对象。
当用户对某个对象发出一个动作时,Windows会通知 Object Pascal产生了一个事件,Object Pascal将自动调用与该对象事件相关的事件过程。即对象和代码之间建立了联系,所以说事件过程是依附于对象的。
另外,一个事件过程也可以被多个对象、多个事件共享。其方法是:首先为其中的一个对象建立事件过程,然后在创建其他对象事件过程时,从事件名称右边的下拉列表中选择已建立的事件即可。
5.2 常用过程与函数数学运算函数用于各种数学运算。常用的数学运算函数有如下几种。
1,绝对值函数
function Abs(X);
返回参数 X 的绝对值。参数 X 可以是整型或实型,返回值为非负的整数实数。
2,平方与平方根函数
function Sqr(X,Extended),Extended;
参数 X 为实型表达式,返回 X 的平方,返回值也是实型数据。
function Sqrt(X,Extended),Extended;
参数 X 为非负实型表达式,返回 X 的算术平方根,返回值也是实型数据。
为了尽可能地减少程序员编写应用程序的工作量,Object Pascal提供了大量的过程与函数 。 下面分类介绍一些常用的过程与函数 。
5.2.1 数学运算函数
5.2.1 数学运算函数
3,三角函数
function Sin(X,Extended),Extended;
function Cos(X,Extended),Extended;
function ArcTan(X,Extended),Extended;
4,取整数部分与取小数部分函数
function Int(X,Extended),Extended;
function Frac(X,Extended),Extended;
5,取整函数
function Trunc(X,Extended),Int64;
function Round(X,Extended),Int64;
6,指数函数和对数函数
function Exp(X,Real):Real;
function Ln(x:Real):Real;
7,随机函数
function Random [ ( Range,Integer) ];
8,π函数
Object Pascal提供了一个返回圆周率的函数,该函数定义为:
function Pi:Extended;
5.2.2 字符处理函数 (1)
1.大小写转换函数
function LowerCase(const S,string),string;
function UpperCase(const S,string),string;
2.合并字符串函数
procedure AppendStr(var Dest,string; const S,string);
function Concat(S1 [,S2,...,Sn],string),string;
3,查找字符串函数
function Pos(Substr,string; S,string),Integer;
4,求字符串长度函数
function Length(S),Integer;
5,数值与字符串转换函数
procedure Str(X [,Width [,Decimals ]]; var S);
procedure Val(S; var V; var Code,Integer);
function IntToStr(Value,Integer),string;
function FloatToStr(Value,Extended),string;
function StrToInt(const S,string),Integer;
function StrToFloat(const S,string),Extended;
5.2.2 字符处理函数 (2)
6,格式化函数
function Format(const Format,string; const Args,array of const),string;
用来将字符串按指定的格式返回。其中第一个参数 format 是“格式字符串”,可以包含字符信息,也可以包含数组 Args中参数的类型和格式信息。第二个参数是一个数组,从左往右每个元素与第一个参数中的类型格式信息一一对应。
类型和格式信息由字符,%”开头,格式如下:
%[<参数位置 >:][-][<所占宽度 >][.<小数位数 >]<类型 >
若缺省参数位置,则从左到右依次分配各参数。,%”之后跟参数位置则必须跟“:”,参数位置指明了当前格式用于第几个数组参数。
5.2.3 对话框函数与对话框过程 (1)
对于一些简单的输入和输出,Windows程序最常用的方法是使用对话框,对话框是用户与应用程序交换信息最方便的途径之一 。 Delphi
7.0提供了多个对话框标准函数供程序员使用,常用的有以下几个函数与过程 。
图 4-1 静态数组
1,ShowMessage过程
Delphi 7.0提供了一个用于显示消息的标准过程,定义如下:
procedure ShowMessage(const Msg,string);
ShowMessage过程的参数是一个字符串常量。 ShowMessage过程的作用是显示一个最简单的对话框,对话框以应用程序的执行文件名作为标题,对话框右上侧是一个关闭按钮,在对话框中显示了字符串常量,也就是用户要显示的信息内容,可以使用硬回车符(# 13)使文本换行。在对话框下部是一个 OK按钮,单击按钮关闭对话框返回应用程序。
2,ShowMessageFmt过程
Delphi 7.0提供了另一个用于显示消息的标准过程,定义如下:
procedure ShowMessageFmt(const Msg,string; Params,array of const);
ShowMessageFmt过程的参数是一个格式字符串和一个数组,与 Format函数相似,可以将用户显示的信息按一定格式显示在对话框中。
5.2.3 对话框函数与对话框过程 (2)
【 例 5-2】 设计程序:利用对话框实现输入球的半径,输出球的体积与表面积。
分析:利用编辑框输入球的半径,计算球的体积可利用数学公式 V=4πr3/3,计算球的表面积可利用数学公式 S=4πr2,其中 π的值可以通过 Pi函数取得。
启动 Delphi 7.0,新建一个工程,在窗体中添加一个标签 Label1,一个编辑框 Edit1和一个按钮 Button1,将 Label1的 Caption属性值改为“输入球的半径:”,将 Edit1的 Text属性值改为,1”,将 Button1的 Caption属性值改为“计算”。如图 5-3所示。
5-3 计算球的体积和表面积
5.2.3 对话框函数与对话框过程 (3)
图 4-1 静态数组双击“计算”按钮,在事件处理过程中添加变量说明和过程代码:
procedure TForm1.Button1Click(Sender,TObject);
var
r,V,S:real;
begin
r:=strtofloat(edit1.Text );
V:=4/3*Pi*r*r*r;
S:=4*Pi*r*r;
ShowMessageFmt(‘球的体积是,%12.4f’+#13+‘球的表面积 是,%12.4f',[V,S]);
end;
保存工程并运行,当单击“计算”按钮时,弹出如图 5-3所示的对话框,
单击 OK按钮后对话框关闭。可以输入任意实数,计算球的体积和表面积。
5.2.3 对话框函数与对话框过程 (4)
3,InputBox函数
Delphi 7.0提供了一个用于输入信息的标准函数,定义如下:
function InputBox(const ACaption,APrompt,ADefault,string),string;
该函数显示一个能接受用户输入的对话框,并以字符串的形式返回用户输入的信息。
InputBox函数有三个参数,第一个参数 Acaption是对话框的标题,第二个参数
Aprompt是提示信息的内容,第三个参数 Adefault指定了默认的输入内容。
将 【 例 5-2】 中的 r:=strtofloat(edit1.Text )语句改为如下语句:
r:=strtofloat(inputbox('输入半径 ','input:','1'));
保存并运行工程,当单击“计算”按钮时弹出如图 5-4所示对话框。
其中对话框的标题为“输入半径”,提示内容为,input:”,输入框中的默认输入内容为“1”,可以对输入内容进行更改。在对话框中有两个按钮,若用户单击 OK按钮则输入内容为函数返回值,若用户单击 Cancel按钮则默认值为函数返回值。
图 5-4 对话框
5.2.3 对话框函数与对话框过程 (5)
4,MessageDlg函数
Object Pascal提供了一个用于显示信息对话框的标准函数,定义如下:
function MessageDlg(const Msg,string; DlgType,TMsgDlgType; Buttons,
TMsgDlgButtons; HelpCtx,Longint),Word;
该函数可以显示一个对话框提示用户,返回值为 Word类型的数据。
MessageDlg函数的参数有四个,第一个参数 Msg用于在对话框中显示相关提示信息,第二个参数 DlgType用于指定对话框的类型,取值及含义如表 5-
2所示。
各个参数的取值及含义见本教程 P68页
5.3 子程序的参数当程序代码在调用一个过程或函数时,通常用参数传递数据到被调用的过程或函数中 。 形式参数是指在定义过程或函数时,出现在过程或函数参数表中的变量名,表示用于接收数据的变量,也简称形参 。 实际参数是指在调用过程或函数时,传送给过程或函数的常量,变量或表达式,也简称实参 。
调用过程与函数的目的,就是要完成某一工作。调用者要把条件告诉过程与函数,过程与函数要把结果传回调用者。通常在编制一个子程序时,要考虑它需要哪些输入量,进行处理和运算后要输出哪些输出量。正确地提供输入输出是子程序使用的关键问题。
5.3.1 形式参数与实际参数对实参有以下几点说明:
( 1)实参列表与形参列表中对应的变量名不必相同,但实参个数、类型必须与形参相一致,否则会发生类型不匹配错误;
( 2)在调用过程或函数时,实参的书写顺序必须与过程或函数定义中相应形参的顺序相同,多个实参则各参数间用逗号隔开,调用时,第一个形参接收第一个实参的值,第二个形参接收第二个实参的值,… 。
5.3.2 参数传递方式(1)
按照参数传递方式的不同,Object Pascal中过程与函数的参数可以分为变量参数,数值参数,常量参数和外部参数四种 。
1,变量参数在过程或函数首部形参列表中的参数前面如果使用保留字 var进行说明,则表示该参数为变量参数。
变量参数传递方式为“按地址传递”,即将实参的地址传递给形参,这样形参与实参表示同一个储存单元,在过程或函数的运算过程中如果改变了形参(也就是实参)的值,这个值的改变将影响到过程或函数调用之后。
假设有以下一个函数的声明:
function DoubleByRef(var X,Integer),Integer; //X是一个变量参数
begin
X,= X * 2;
Result,= X;
end;
该函数定义中声明了一个变量参数 X,在函数运算中将 X的值扩大了一倍,
改变了参数的值。
5.3.2 参数传递方式(2)
在调用该函数后实参的值改变如下:
Var a,b,c,Integer;
begin
a:= 4;
b:=DoubleByRef(a); //执行后 b = 8,a = 8
//调用函数后值的改变将影响到函数调用之后。此时 a的值为 8
c:=DoubleByRef(a); //执行后 c = 16,a = 16
end;
在编程中应注意观察过程或函数对变量参数的操作,也可以用变量参数将处理结果返回。
【 例 5-3】 编写一个交换两个实数的过程。
分析:过程的功能是实现两个实数的交换。过程的输入为两个实数,要将交换后的结果返回可以利用变量参数实现。
代码如下:
procedure swap(var a,b,real);
var c:real;
begin
c:=a;a:=b;b:=a;end;
5.3.2 参数传递方式(3)
2,数值参数在过程或函数首部形参列表中的参数前面如果没有保留字进行说明,则表示该参数为数值参数。
数值参数的传递方式为“按值传递”,即将实参的值传递给形参,如果在过程或函数中改变了形参的值,不会影响实参变量。相当于在调用过程或函数时,重新开辟一个存储单元,将实参值赋予该存储单元,并对该单元进行运算和处理,过程或函数调用结束后将存储单元释放。
3,常量参数在过程或函数首部形参列表中的参数前面如果使用保留字 const进行说明,则表示该参数为常量参数。
对常量参数而言,不论实参是常量或变量,在过程或函数的调用中数值总是不能被改变。常量参数就像局部常量或只读变量,在过程或函数主体中不能对常量参数赋值,也不能将其作为变量参数传递给其他过程或函数。
使用 const为变量提供了安全保障,确保实参的值不被改变。
5.3.2 参数传递方式(4)
4,外部参数在过程或函数首部形参列表中的参数前面如果使用保留字 out进行说明,
则表示该参数为外部参数。
外部参数与变量参数一样,其传递方式为“按地址传递”,但外部参数与变量参数使用时有很大区别。对于 out参数,变量的初始值被过程或函数丢弃,out参数仅用于输出,也就是说,它告诉函数或过程在哪里存储输出,而不提供任何输入。
out参数经常用于分布式对象模块,如 COM和 CORBA。此外,向函数或过程传递未初始化的变量时,也可以考虑使用 out参数。
5.3.3 使用缺省参数(1)
在过程或函数首部可以指定缺省参数的值。缺省值仅允许用于有类型的常量参数和数值参数。要提供缺省值,需要在参数声明的末尾使用等号
( =)并跟随一个与参数类型赋值兼容的常量表达式。
例如,给出如下声明:
procedure SampleProcedure(x:real,y:real=10);
begin
…
end;
对上述过程,下面的两个语句是等效的:
SampleProcedure(a);
SampleProcedure(a,10);
在程序调用中指定值将覆盖在过程或函数定义中指定的缺省值。因此,对于
5.3.3 使用缺省参数(2)
如下声明:
SampleProcedure(a,20);
第二个参数传入的值是 20。
多个参数的声明不能指定一个缺省值。因此,下面的声明:
function MyFunction(X,Real = 3.5; Y,Real = 3.5),Real;
是合法的,而下面的声明:
function MyFunction(X,Y,Real = 3.5),Real; //语法错误是不合法的。
含有缺省值的参数必须出现参数列表的末尾。简而言之,跟随在第一个声明了缺省值的参数之后的所有参数都必须也有缺省值。因此,下面的声明是不合法的:
procedure MyProcedure(I,Integer = 1; S,string);
5.3.4 数组参数声明接受数组参数的过程时,不能在参数声明中包括类型说明。下面的声明将导致编译错误:
procedure Sort(A,array[1..10] of Integer); //语法错误而下面的声明则是有效的:
type TDigits = array[1..10] of Integer;
procedure Sort(A,TDigits);
在一个过程与函数中包含另外一个过程与函数,称为子程序的嵌套。
子程序不仅可以调用其他子程序,还可以调用自身,子程序直接调用自身称为直接递归,子程序间接调用自身称为间接递归。
在解决实际问题时经常利用过程与函数的嵌套与递归使复杂问题简单化。
5.4 子程序的嵌套与递归
5.4.1 子程序的嵌套在 Delphi 7.0中子程序有一定的层次结构,在一个子程序的内部可以再定义一个新的子程序,即子程序可以嵌套定义。
子程序的嵌套要求外层子程序能够完全包含内层子程序,不允许局部包含,
即不允许出现第一个子程序的定义还没有结束,又开始一个定义新的子程序。
子程序不但可以嵌套定义也可以嵌套调用,即在一个子程序中调用另一个子程序。
在 Object Pascal中,过程与函数必须先说明再调用,子程序的调用必须遵守以下规则:
( 1)子程序可以调用其内层的子程序但不能隔层调用,如图 5-5所示,P2
可以调用 P2A和 P2B,但不能调用 P2AB;
( 2)内层子程序可以调用外层的子程序,而且允许隔层调用。如 P1A可以调用 P1,P2B可以调用 P2;
( 3)同一层的子程序,允许后定义的子程序调用先定义的子程序,如 P1B
可以调用 P1A,P2可以调用 P1,但 P2A不能调用 P1A;
Uint1P1P1AP1BP2P2AP2BP2AB
( 4)如果需要调用同层中后定义的子程序,可以用保留字 forward(超前引用)进行前置说明,即在还未进行代码实现的过程与函数的声明部分最后加上保留字 forward。
5.4.2 子程序的递归(1)
递归的主要特点是子程序直接或间接地调用自身。递归在算法描述中有非常重要的作用,许多复杂的问题都可以通过递归得到解决,递归在处理阶乘运算、级数运算、幂指数运算等方面特别有效。
直接递归调用的例子如下:
function fun(n:integer):integer;
var a,b:integer;
begin
…
a:=fun(b);
…
end;
明显地,在编写程序中不应当出现无休止地递归调用,而只能出现有限次数的调用,这可以通过程序流程控制来实现。
【 例 5-4】 定义一个求自然数 n的阶乘的函数。
5.4.2 子程序的递归(2)
分析:计算一个自然数 n的阶乘可以采用直接相乘的方法,n乘以 ( n-1) 再乘以 ( n-2) …,一直乘到 1,这是比较容易理解的一种方法,可以利用循环语句方便地实现 。 还可以利用递归的方法,求自然数 n的阶乘也可写成如下数学定义:
求解 n的阶乘可以利用 (n-1)的阶乘来实现,如 5! =5*4!,而 4!
=4*3! …,直到 1! =1,即可得到最终结果。
实现代码如下:
function mult(n:integer):integer;
begin
if n=0 then
mult:=1
else mult:=n*mult(n-1);
end;
对于间接递归,最简单的一种情况是函数 A调用函数 B,而函数 B又调用函数 A的情况。此时函数要进行前置说明,即在还未进行代码实现的过程与函数的声明部分最后加上保留字 forward。(俱体分析见本教程 P74页)
0)!1(
01!
nnn
nn
5.5 变量的作用域变量的作用域是指变量能被某一子程序识别的范围 。 在一个工程的某一部分声明一个变量之后,这个被声明的变量是不是在程序中处处可用? 答案是否定的,每个变量都有它的作用范围 。
5.5.1 公有变量与私有变量在单元的接口部分声明的变量属于公有变量,该变量不但能被该单元中的过程与函数引用,也可以被其他单元引用。
一个单元需引用其他单元的变量时,只要在 uses语句中指明其他单元即可。
新建一个工程,观察 unit1中的 uses语句,
uses
Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,
Forms,Dialogs;
对于该单元可以引用 Windows,Messages,SysUtils等单元接口部分声明的变量。
对一个单元,在关键字 Interface(接口)之后关键字 Implementation(实现)之前声明的变量、类型或过程都是可以被其他单元引用的。
在关键字 Implementation(实现)之后声明的变量属于私有变量,它只被该单元访问,
不能被其他单元访问,相当于是该单元的私有部分。
5.5.2 全局变量与局部变量(1)
在所有子程序声明之前,在关键字 Implementation(实现)之后声明的变量属于全局变量,在关键字 Interface(接口)部分声明的变量也属于全局变量。
全局变量的作用域是整个单元,即在单元中声明的所有子程序中都是可见的和有定义的,可以对其操作或引用。而局部变量的作用域是单个子程序,在单个子程序中它是有定义的,在该子程序之外它不能被其他子程序引用或访问。
在一个子程序内部定义的变量是内部变量,它只在该子程序范围内有效,
也就是说只有在该子程序内部才能使用它们,在此子程序之外是不能使用这些变量的,这种变量称为局部变量 。
注意,Delphi 7.0中不能在程序中声明变量,只能在程序开始之前对变量进行声明
unit Unit1;
interface
implementation
{$R*.dfm}
function…
…
V1
V2
V3
5.5.2 全局变量与局部变量(2)
如图 5-6所示,在 V1部分声明的变量属于公有变量,V2,V3部分声明的变量属于私有变量;而 V1,V2部分声明的变量属于全局变量,V3部分声明的变量属于局部变量。公有私有是相对外部单元而言,而全局、局部是相对单元内部而言。